1 module sbylib.math.angle; 2 3 import std.format : format; 4 5 /** 6 This struct is like (Degree | Radian) 7 */ 8 struct Angle { 9 private float deg; 10 11 package this(float deg) { 12 this.deg = deg; 13 } 14 15 /** 16 unary operator for '-' 17 18 Returns: negated angle struct 19 */ 20 Angle opUnary(string op)() const 21 if (op == "-") 22 { 23 return Angle(mixin(op ~ "this.deg")); 24 } 25 26 /** 27 binary operator between float ("*" and "/" only) 28 29 Params: 30 v = operation target value 31 32 Returns: calculated angle struct 33 */ 34 Angle opBinary(string op)(float v) const 35 if (op == "*" || op == "/") 36 { 37 return Angle(mixin("this.deg " ~ op ~ " v")); 38 } 39 40 /** 41 binary operator between float ("*" and "/" only) 42 43 Params: 44 v = operation target value 45 46 Returns: calculated angle struct 47 */ 48 Angle opBinaryRight(string op)(float v) const 49 if (op == "*" || op == "/") 50 { 51 return Angle(mixin("v" ~ op ~ "this.deg")); 52 } 53 54 /** 55 binary operator between Angle ("*" and "/" only) 56 57 Params: 58 a = operation target 59 60 Returns: calculated angle struct 61 */ 62 Angle opBinary(string op)(Angle a) const { 63 return Angle(mixin("this.deg " ~ op ~ " a.deg")); 64 } 65 66 /** 67 operator assign between Angle ("*" and "/" only) 68 69 Params: 70 a = operation target 71 */ 72 void opOpAssign(string op)(float v) 73 if (op == "*" || op == "/") 74 { 75 mixin("this.deg" ~ op ~ "= v;"); 76 } 77 78 /** 79 operator assign between Angle ("*" and "/" only) 80 81 Params: 82 a = operation target 83 */ 84 void opOpAssign(string op)(Angle a) { 85 mixin("this.deg" ~ op ~ "= a.deg;"); 86 } 87 88 /** 89 compare operation between Angle 90 91 Params: 92 a = comparison target 93 94 Returns: comparison result 95 */ 96 int opCmp(Angle a) const { 97 import std.math : sgn; 98 99 return cast(int)sgn(this.deg - a.deg); 100 } 101 102 /** 103 equal operation between Angle 104 105 Params: 106 a = comparison target 107 108 Returns: comparison result 109 */ 110 bool opEquals(Angle a) const { 111 return this.deg == a.deg; 112 } 113 114 /** 115 Calculate the hash value for this instance. 116 It is equals to the floating degree expression's hash value. 117 118 Returns: hash value 119 */ 120 int toHash() const { 121 return this.deg.deg.toHash(); 122 } 123 124 /** 125 convers to String 126 127 Returns: String expression 128 */ 129 string toString() const { 130 return format!"%f [deg.]"(deg); 131 } 132 133 /** 134 Get the floating value as radians. 135 136 Returns: radian value 137 */ 138 float asRadian() const { 139 import std.math : PI; 140 return deg * PI / 180; 141 } 142 143 /** 144 Get the floating value as degrees. 145 146 Returns: degree value 147 */ 148 float asDegree() const { 149 return deg; 150 } 151 } 152 153 private mixin template RadianInputFunction(string func) { 154 import std : replace; 155 mixin(q{ 156 auto ${func}(const Angle angle) { 157 import std.math : ${func}; 158 return ${func}(angle.asRadian()); 159 } 160 }.replace("${func}", func)); 161 } 162 163 private mixin template RadianOutputFunction(string func) { 164 import std : replace; 165 mixin(format!q{ 166 auto ${func}(float angle) { 167 import std.math : ${func}; 168 return ${func}(angle).rad; 169 } 170 }.replace("${func}", func)); 171 } 172 173 private mixin template AngleInputFunction(string func) { 174 import std : replace; 175 mixin(format!q{ 176 auto ${func}(const Angle angle) { 177 import std.math : ${func}; 178 return ${func}(angle.asDegree()).deg; 179 } 180 181 }.replace("${func}", func)); 182 } 183 184 mixin RadianInputFunction!("sin"); 185 mixin RadianInputFunction!("cos"); 186 mixin RadianInputFunction!("tan"); 187 mixin RadianOutputFunction!("asin"); 188 mixin RadianOutputFunction!("acos"); 189 mixin RadianOutputFunction!("atan"); 190 mixin AngleInputFunction!("abs"); 191 192 /** 193 Get Angle struct from degree value. 194 195 Returns: Angle struct that equals to the degree value. 196 */ 197 Angle deg(float d) { 198 return Angle(d); 199 } 200 201 /** 202 Get Angle struct from radian value. 203 204 Returns: Angle struct that equals to the radian value. 205 */ 206 Angle rad(float r) { 207 import std.math : PI; 208 return Angle(r * 180 / PI); 209 }