1 /*physics.d by Ruby The Roobster*/ 2 /*Version 1.0.0 release*/ 3 /*Module for basic physics in the D Programming Language 2.0*/ 4 /*This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 3 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>.*/ 16 /** Copyright: 2021, Ruby The Roobster*/ 17 /**Author: Ruby The Roobster, rubytheroobster@yandex.com*/ 18 /**Date: November 8, 2021*/ 19 /** License: GPL-3.0*/ 20 21 ///NOTICE: THIS CODE OBVIOUSLY SUCKS. IT WILL BE DELETED AND REPLACED WITH SOMETHING ACTUALLY USUABLE. 22 23 module dutils.physics; 24 version(DLL) 25 { 26 27 } 28 else 29 { 30 public import dutils.skeleton; 31 32 ///Struct for representing gravity. 33 public struct Gravity 34 { 35 /** The axis which the gravity pulls toward.*/ Axis axis = Axis.y; 36 /** The strength per frame of the gravity. */ real strength = 0; 37 } 38 39 ///Enumeration for representing an axis. 40 public enum Axis { /**The x-axis.*/ x, /**The y-axis.*/y, /**The z-axis.*/ z} 41 ///Enumeration for representing a plane. 42 public enum Plane { /**Plane xy*/ xy, /**Plane xz*/ xz, /**Plane zy*/ zy} 43 44 package mixin template __mov__general__(string func) 45 { 46 void __mov__general__(real accdec = 0, Gravity gravity = Gravity(Axis.y, 0)) 47 { 48 import core.thread; 49 Point moveby; 50 bool b = false; 51 auto ori = tomove; 52 debug import std.stdio : writeln; 53 if(speed > 1 || speed < -1) 54 { 55 moveby.x = moveto.x / speed; 56 moveby.y = moveto.y / speed; 57 moveby.z = moveto.z / speed; 58 b = true; 59 } 60 else 61 { 62 moveby.x = moveto.x * speed; 63 moveby.y = moveto.y * speed; 64 moveby.z = moveto.z * speed; 65 } 66 while(!((tomove.center.x > moveto.x && moveto.x > 0) ^ (tomove.center.x < moveto.x && moveto.x < 0))) 67 { 68 static if(func[0] == 'a') 69 { 70 speed += accdec; 71 if(b) 72 { 73 moveby.x = moveto.x / speed; 74 moveby.y = moveto.y / speed; 75 moveby.z = moveto.z / speed; 76 } 77 else 78 { 79 moveby.x = moveto.x * speed; 80 moveby.y = moveto.y * speed; 81 moveby.z = moveto.z * speed; 82 } 83 } 84 else static if(func[0] == 'd') 85 { 86 speed -= accdec; 87 if(b) 88 { 89 moveby.x = moveto.x / speed; 90 moveby.y = moveto.y / speed; 91 moveby.z = moveto.z / speed; 92 } 93 else 94 { 95 moveby.x = moveto.x * speed; 96 moveby.y = moveto.y * speed; 97 moveby.z = moveto.z * speed; 98 } 99 } 100 else 101 { 102 } 103 foreach(i;tomove.faces) 104 { 105 foreach(k;i.lines) 106 { 107 foreach(j;k.mid_points) 108 { 109 j += moveby; 110 } 111 k.start += moveby; 112 k.stop += moveby; 113 } 114 i.center += moveby; 115 } 116 tomove.center += moveby; 117 Thread.sleep(dur!"msecs"(tbf)); 118 static if(func[0] == 'a') 119 { 120 speed += accdec; 121 } 122 else static if(func[0] == 'd') 123 { 124 speed -= accdec; 125 } 126 else 127 { 128 } 129 } 130 foreach(i;ori.faces) 131 { 132 foreach(j;i.lines) 133 { 134 foreach(k;j.mid_points) 135 { 136 k += moveto; 137 } 138 j.start += moveto; 139 j.stop += moveto; 140 } 141 i.center += moveto; 142 } 143 ori.center += moveto; 144 tomove = ori; 145 } 146 } 147 148 /**move moves all the points in a skeleton to a specified point with a specified time gap between moving the points. 149 Params: 150 moveto = A point specifying the total amount to move along each axis. 151 tbf = The time in miliseconds between 'frames'(a frame is one section of moving points before waiting a bit). This gives an illusion of continuous motion. 152 tomove = The skeleton being moved. 153 speed = The speed at which to move the points. 154 Returns: 155 none*/ 156 pragma(inline, true) public void move(Point moveto, uint tbf, ref shared Skeleton tomove, real speed) 157 { 158 mixin __mov__general__!"n"; 159 __mov__general__(); 160 } 161 162 /**accMove moves all the points in a skeleton to a specified point with a specified time gap between movements all while accelerating the speed. 163 Params: 164 moveto = A point specifying the total amount to move along each axis. 165 tbf = The time in miliseconds between 'frames'(a frame is one section of moving points before waiting a bit). This gives an illusion of continuous motion. 166 tomove = The skeleton being moved. 167 speed = The original speed at which the skeleton moves. 168 accdec = The amount to increment the speed by each frame. 169 Returns: none*/ 170 pragma(inline, true) public void accMove(Point moveto, uint tbf, shared ref Skeleton tomove, real speed, real accdec = 0) 171 { 172 mixin __mov__general__!"a"; 173 __mov__general__(accdec); 174 } 175 176 /**decMove moves all the points in a skeleton to a specified point with a specified time gap between movements all while deaccelerating the speed. 177 Params: 178 moveto = A point specifying the total amount to move along each axis. 179 tbf = The time in miliseconds between 'frames'(a frame is one section of moving points before waiting a bit). This gives an illusion of continuous motion. 180 tomove = The skeleton being moved. 181 speed = The original speed at which the skeleton moves. 182 accdec = The amount to decrement the speed by each frame. 183 Returns: none*/ 184 pragma(inline) public void decMove(Point moveto, uint tbf, shared ref Skeleton tomove, real speed, real accdec = 0) 185 { 186 mixin __mov__general__!"d"; 187 __mov__general__(accdec); 188 } 189 190 ///Collision is a structure representing if a collision happened, and the object collided with. 191 public struct Collision 192 { 193 ///True if the collision actually occured. 194 bool collided; 195 ///The skeleton collided with. 196 shared Skeleton hitby; 197 } 198 199 /**detectCollision takes a skeleton, a wait time, and an array of skeletons, and detects collisions, returning true if so. 200 Params: 201 towatch = A shared array of skeletons that the functions dectects collisions against. 202 skele = A skeleton that the function dectects collisions against the array of skeletons with. 203 time = The number of miliseconds to wait before exiting. Infinete when set to real.infinity. 204 Returns: 205 A collision structure. 206 */ 207 public Collision detectCollision(shared Skeleton[] towatch, shared Skeleton skele, in real time = 0) 208 in { 209 auto a = cast(ulong)time; 210 assert(a == time || time == real.infinity,"Parameter time must always be a whole number or infinity!"); 211 } 212 do { 213 mixin find!(["x", "y", "z"]); 214 import std.datetime.stopwatch; 215 auto sw = StopWatch(AutoStart.no); 216 sw.start(); 217 scope(exit) sw.stop(); 218 while(true) 219 { 220 foreach(i;towatch) 221 { 222 foreach(j;i.faces) 223 { 224 foreach(k;j.lines) 225 { 226 foreach(l;k.mid_points) 227 { 228 foreach(m;skele.faces) 229 { 230 foreach(n;m.lines) 231 { 232 for(uint o; o < n.mid_points.length+1; o++) 233 { 234 235 pragma(inline, true) bool switcho(Point toswitch) 236 { 237 switch(o) 238 { 239 default: 240 if(o == n.mid_points.length) 241 { 242 find([n.mid_points[o-1], n.stop]); 243 return Collision(true, i).collided; 244 } 245 else 246 { 247 find([n.mid_points[o-1], n.mid_points[o]]); 248 } 249 if(toswitch.x <= highx && toswitch.x >= lowx && toswitch.y <= highy && toswitch.y >= lowy && toswitch.z <= highz && toswitch.z >= lowz) 250 { 251 return Collision(true, i).collided; 252 } 253 break; 254 case 0: 255 find([n.start, n.mid_points[o]]); 256 if(toswitch.x <= highx && toswitch.x >= lowx && toswitch.y <= highy && toswitch.y >= lowy && toswitch.z <= highz && toswitch.z >= lowz) 257 { 258 return Collision(true, i).collided; 259 } 260 } 261 assert(false, "Hidden function switcho has a bug, file an issue."); 262 } 263 if(switcho(l)) 264 return Collision(true, i); 265 if(switcho(k.start)) 266 return Collision(true, i); 267 if(switcho(k.stop)) 268 return Collision(true, i); 269 } 270 } 271 } 272 } 273 } 274 } 275 } 276 if(!(sw.peek.total!"msecs" <= time) && time != real.infinity) 277 break; 278 } 279 return Collision(false, skele); 280 } 281 282 package mixin template find(string[] tofind) 283 { 284 static foreach(i; tofind) { 285 mixin("real high" ~ i ~ ";"); 286 mixin("real low" ~ i ~ ";"); 287 } 288 void find(Point[2] tof) 289 { 290 static foreach(i; tofind) 291 { 292 mixin("high" ~ i ~ " = tof[0]." ~ i ~ " >= tof[1]." ~ i ~ " ? tof[0]." ~ i ~ " : tof[1]." ~ i ~ ";"); 293 mixin("low" ~ i ~ " = tof[0]." ~ i ~ " <= tof[1]." ~ i ~ " ? tof[0]." ~ i ~ " : tof[1]." ~ i ~ ";"); 294 } 295 } 296 } 297 298 /**affectByGravity affects a skeleton by a specified gravity struct. Send any integer to the thread containing the function to terminate it. 299 Params: 300 towatch = A shared array of skeletons that is used for collision checking. 301 skele = A shared skeleton that is affected by gravity itself. 302 tbf = The wait time between frames in miliseconds, operations not included. Set to at least 1, as the function spends 1 milisecond waiting for messages. 303 gravity = A gravity struct that gives the axis and strength specifications. 304 Returns: none.*/ 305 pragma(inline, true) public void affectByGravity(shared Skeleton[] towatch, ref shared Skeleton skele, in uint tbf, Gravity gravity) 306 { 307 import std.concurrency; 308 import core.thread; 309 import std.datetime; 310 void __dumbswitchiepoo__dumb(ref shared Point l) 311 { 312 switch(gravity.axis) 313 { 314 case Axis.x: 315 if((gravity.strength > 0 && l.x > 0) ^ (gravity.strength < 0 && l.x < 0) || detectCollision(towatch, skele, 0).collided) 316 l.x = l.x - gravity.strength; 317 break; 318 case Axis.y: 319 if((gravity.strength > 0 && l.y > 0) ^ (gravity.strength < 0 && l.y < 0) || detectCollision(towatch, skele, 0).collided) 320 l.y = l.y - gravity.strength; 321 break; 322 case Axis.z: 323 if((gravity.strength > 0 && l.z > 0) ^ (gravity.strength < 0 && l.z < 0) || detectCollision(towatch, skele, 0).collided) 324 l.z = l.z - gravity.strength; 325 break; 326 default: 327 throw new Exception("An error obviously caused by a library developer has occured. Please file an issue."); 328 } 329 } 330 while(true) 331 { 332 if(receiveTimeout(dur!"msecs"(1), function void(int x) { })) 333 { 334 break; 335 } 336 foreach(ref i;skele.faces) 337 { 338 foreach(ref j;i.lines) 339 { 340 foreach(ref k;j.mid_points) 341 { 342 __dumbswitchiepoo__dumb(k); 343 } 344 __dumbswitchiepoo__dumb(j.start); 345 __dumbswitchiepoo__dumb(j.stop); 346 } 347 __dumbswitchiepoo__dumb(i.center); 348 } 349 __dumbswitchiepoo__dumb(skele.center); 350 Thread.sleep(dur!"msecs"(tbf)); 351 } 352 } 353 354 /**Rotates a skeleton by an amount in radians in anamount of frames with a specifed time between frames. 355 Params: 356 degrees = The number of radians to rotate the Skeleton by. 357 plane = The plane to rotate the Skeleton on. 358 frames = The number of frames to rotate the Skeleton in. 359 rate = The amount of time in miliseconds to wait between processing each frame. 360 skele = The Skeleton to rotate. 361 */ 362 public void rotate(in real degrees, in Plane plane, in ulong frames, in ulong rate, ref shared Skeleton skele) 363 { 364 ulong fcounter = 0; 365 assert(frames != 0); //Prevent division by 0. 366 import std.math.algebraic : abs; 367 import std.math.trigonometry : sin; 368 import std.math.trigonometry: cos; 369 import core.thread; 370 bruh: 371 switch(plane) 372 { 373 static foreach(a; ["xy", "xz", "zy"]) 374 { 375 mixin("case Plane." ~ a ~ ":"); 376 while(fcounter <= frames) 377 { 378 foreach(ref i; skele.faces) 379 { 380 foreach(ref j; i.lines) 381 { 382 foreach(ref k; j.mid_points) 383 { 384 mixin("real radius = abs(cast(real)(skele.center." ~ [a[0]] ~ " - k." ~ [a[0]] ~ "));"); //Define the radius of the circle of rotation. 385 //Do the rotation thingy. 386 mixin("k." ~ [a[0]] ~ " = (cos(degrees/frames) * radius) + skele.center." ~ [a[0]] ~ ";"); 387 //As above 388 mixin("radius = abs(cast(real)(skele.center." ~ [a[1]] ~ " - k." ~ [a[1]] ~ "));"); 389 mixin("k." ~ [a[1]] ~ " = (sin(degrees/frames) * radius) + skele.center." ~ [a[1]] ~ ";"); 390 } 391 //Rinse and Repeat! 392 mixin("real radius = abs(cast(real)(skele.center." ~ [a[0]] ~ " - j.start." ~ [a[0]] ~ "));"); 393 mixin("j.start." ~ [a[0]] ~ " = (cos(degrees/frames) * radius) + skele.center." ~ [a[0]] ~ ";"); 394 mixin("radius = abs(cast(real)(skele.center." ~ [a[1]] ~ " - j.start." ~ [a[1]] ~ "));"); 395 mixin("j.start." ~ [a[1]] ~ " = (sin(degrees/frames) * radius) + skele.center." ~ [a[1]] ~ ";"); 396 mixin("radius = abs(cast(real)(skele.center." ~ [a[0]] ~ " - j.stop." ~ [a[0]] ~ "));"); 397 mixin("j.stop." ~ [a[0]] ~ " = (cos(degrees/frames) * radius) + skele.center." ~ [a[0]] ~ ";"); 398 mixin("radius = abs(cast(real)(skele.center." ~ [a[1]] ~ " - j.stop." ~ [a[1]] ~ "));"); 399 mixin("j.stop." ~ [a[1]] ~ " = (sin(degrees/frames) * radius) + skele.center." ~ [a[1]] ~ ";"); 400 } 401 mixin("real radius = abs(cast(real)(skele.center." ~ [a[0]] ~ " - i.center." ~ [a[0]] ~ "));"); 402 mixin("i.center." ~ [a[0]] ~ " = (cos(degrees/frames) * radius) + skele.center." ~ [a[0]] ~ ";"); 403 mixin("radius = abs(cast(real)(skele.center." ~ [a[1]] ~ " - i.center." ~ [a[1]] ~ "));"); 404 mixin("i.center." ~ [a[1]] ~ " = (sin(degrees/frames) * radius) + skele.center." ~ [a[1]] ~ ";"); 405 } 406 Thread.sleep(dur!"msecs"(rate)); //Time between frames. 407 ++fcounter; 408 } 409 mixin("break bruh;"); 410 } 411 default: 412 throw new Exception("Invalid Plane, somehow. Either I screwed up or you did. If you didn't touch the code, open an issue."); 413 } 414 } 415 } 416