1 /* 2 Copyright © 2022, Inochi2D Project 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 5 Author Asahi Lina 6 */ 7 module inochi2d.phys.system; 8 import inochi2d; 9 import std.math : isFinite; 10 11 abstract class PhysicsSystem { 12 private: 13 ulong[float*] variableMap; 14 float*[] refs; 15 float[] derivative; 16 17 float t; 18 19 protected: 20 /** 21 Add a float variable to the simulation 22 */ 23 ulong addVariable(float* var) { 24 ulong index = refs.length; 25 26 variableMap[var] = index; 27 refs ~= var; 28 29 return index; 30 } 31 32 /** 33 Add a vec2 variable to the simulation 34 */ 35 ulong addVariable(vec2* var) { 36 ulong index = addVariable(&(var.vector[0])); 37 addVariable(&(var.vector[1])); 38 return index; 39 } 40 41 /** 42 Set the derivative of a variable (solver input) by index 43 */ 44 void setD(ulong index, float value) { 45 derivative[index] = value; 46 } 47 48 /** 49 Set the derivative of a float variable (solver input) 50 */ 51 void setD(ref float var, float value) { 52 ulong index = variableMap[&var]; 53 setD(variableMap[&var], value); 54 } 55 56 /** 57 Set the derivative of a vec2 variable (solver input) 58 */ 59 void setD(ref vec2 var, vec2 value) { 60 setD(var.vector[0], value.x); 61 setD(var.vector[1], value.y); 62 } 63 64 float[] getState() { 65 float[] vals; 66 67 foreach(idx, ptr; refs) { 68 vals ~= *ptr; 69 } 70 71 return vals; 72 } 73 74 void setState(float[] vals) { 75 foreach(idx, ptr; refs) { 76 *ptr = vals[idx]; 77 } 78 } 79 80 /** 81 Evaluate the simulation at a given time 82 */ 83 abstract void eval(float t) { 84 } 85 86 public: 87 /** 88 Run a simulation tick (Runge-Kutta method) 89 */ 90 void tick(float h) { 91 float[] cur = getState(); 92 float[] tmp; 93 tmp.length = cur.length; 94 derivative.length = cur.length; 95 foreach(i; 0..derivative.length) 96 derivative[i] = 0; 97 98 eval(t); 99 float[] k1 = derivative.dup; 100 101 foreach(i; 0..cur.length) 102 *refs[i] = cur[i] + h * k1[i] / 2f; 103 eval(t + h / 2f); 104 float[] k2 = derivative.dup; 105 106 foreach(i; 0..cur.length) 107 *refs[i] = cur[i] + h * k2[i] / 2f; 108 eval(t + h / 2f); 109 float[] k3 = derivative.dup; 110 111 foreach(i; 0..cur.length) 112 *refs[i] = cur[i] + h * k3[i]; 113 eval(t + h); 114 float[] k4 = derivative.dup; 115 116 foreach(i; 0..cur.length) { 117 *refs[i] = cur[i] + h * (k1[i] + 2 * k2[i] + 2 * k3[i] + k4[i]) / 6f; 118 if (!isFinite(*refs[i])) { 119 // Simulation failed, revert 120 foreach(j; 0..cur.length) 121 *refs[j] = cur[j]; 122 break; 123 } 124 } 125 126 t += h; 127 } 128 129 public: 130 131 abstract void drawDebug(mat4 trans = mat4.identity); 132 } 133