Created
May 21, 2024 02:30
-
-
Save forrestthewoods/bfecfdb96125f8e66ad2d98bf7d49b94 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SIQuantity :: struct($DataType: Type, | |
$LengthNum: int, $LengthDenom: int, $LengthExp: int, | |
$MassNum: int, $MassDenom: int, $MassExp: int, | |
$TimeNum: int, $TimeDenom: int, $TimeExp: int) | |
{ | |
amount : DataType; | |
} | |
Meters :: #bake_arguments SIQuantity( | |
LengthNum=1, LengthDenom=1, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=0, TimeDenom=0, TimeExp=0); | |
Seconds :: #bake_arguments SIQuantity( | |
LengthNum=0, LengthDenom=0, LengthExp=0, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=1, TimeDenom=1, TimeExp=1); | |
Meters2 :: #bake_arguments SIQuantity( | |
LengthNum=1, LengthDenom=1, LengthExp=2, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=0, TimeDenom=0, TimeExp=0); | |
MetersPerSecond :: #bake_arguments SIQuantity( | |
LengthNum=1, LengthDenom=1, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=1, TimeDenom=1, TimeExp=-1); | |
MetersPerSecond2 :: #bake_arguments SIQuantity( | |
LengthNum=1, LengthDenom=1, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=1, TimeDenom=1, TimeExp=-2); | |
Meters2PerSecond2 :: #bake_arguments SIQuantity( | |
LengthNum=1, LengthDenom=1, LengthExp=2, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=1, TimeDenom=1, TimeExp=-2); | |
Kilometers :: #bake_arguments SIQuantity( | |
LengthNum=1000, LengthDenom=1, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=0, TimeDenom=0, TimeExp=0); | |
KilometersPerSecond :: #bake_arguments SIQuantity( | |
LengthNum=1000, LengthDenom=1, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=1, TimeDenom=1, TimeExp=-1); | |
KilometersPerSecond2 :: #bake_arguments SIQuantity( | |
LengthNum=1000, LengthDenom=1, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=1, TimeDenom=1, TimeExp=-2); | |
find_param :: (ti: *Type_Info_Struct, name: string, $T: Type) -> T { | |
placeholder: T; | |
for param: ti.specified_parameters { | |
if param.name == name { | |
if param.type != type_info(T) { | |
print("\nFATAL ERROR: find_param(%, %) expected type [%] but found [%]\n", ti.name, name, T, param.type.type); | |
assert(false); | |
return placeholder; | |
} | |
value := (cast(*T) *ti.constant_storage[param.offset_into_constant_storage]).*; | |
return value; | |
} | |
} | |
print("FATAL ERROR: Failed to find parameter [%]\n", name); | |
assert(false); | |
return placeholder; | |
} | |
operator + :: (a:$T/SIQuantity, b:T) -> T | |
{ | |
result : T; | |
result.amount = a.amount + b.amount; | |
return result; | |
} | |
operator - :: (a:$T/SIQuantity, b:T) -> T | |
{ | |
result : T; | |
result.amount = a.amount - b.amount; | |
return result; | |
} | |
operator * :: (a:$T/SIQuantity, b:$U/SIQuantity) -> $RET | |
#modify { | |
// Extract polymorphic constants from TypeInfo | |
ti1 := cast(*Type_Info_Struct)(T); | |
datatype_a := find_param(ti1, "DataType", Type); | |
ln1 := find_param(ti1, "LengthNum", int); | |
ld1 := find_param(ti1, "LengthDenom", int); | |
le1 := find_param(ti1, "LengthExp", int); | |
mn1 := find_param(ti1, "MassNum", int); | |
md1 := find_param(ti1, "MassDenom", int); | |
me1 := find_param(ti1, "MassExp", int); | |
tn1 := find_param(ti1, "TimeNum", int); | |
td1 := find_param(ti1, "TimeDenom", int); | |
te1 := find_param(ti1, "TimeExp", int); | |
ti2 := cast(*Type_Info_Struct)(U); | |
datatype_b := find_param(ti2, "DataType", Type); | |
ln2 := find_param(ti2, "LengthNum", int); | |
ld2 := find_param(ti2, "LengthDenom", int); | |
le2 := find_param(ti2, "LengthExp", int); | |
mn2 := find_param(ti2, "MassNum", int); | |
md2 := find_param(ti2, "MassDenom", int); | |
me2 := find_param(ti2, "MassExp", int); | |
tn2 := find_param(ti2, "TimeNum", int); | |
td2 := find_param(ti2, "TimeDenom", int); | |
te2 := find_param(ti2, "TimeExp", int); | |
// Ensure we aren't mixing storage types | |
if datatype_a != datatype_b { | |
msg := tprint("\nFATAL ERROR: Quantities must have same DataType.\n T: [%]\n U: [%]", T, U); | |
return false, msg; | |
} | |
// Ratios must be same OR one must be zero | |
ln_ok := ln1 == ln2 || ln1 == 0 || ln2 == 0; | |
ld_ok := ld1 == ld2 || ld1 == 0 || ld2 == 0; | |
mn_ok := mn1 == mn2 || mn1 == 0 || mn2 == 0; | |
md_ok := md1 == md2 || md1 == 0 || md2 == 0; | |
tn_ok := tn1 == tn2 || tn1 == 0 || tn2 == 0; | |
td_ok := td1 == td2 || td1 == 0 || td2 == 0; | |
if !ln_ok || !ld_ok || !mn_ok || !md_ok || !tn_ok || !td_ok { | |
msg := tprint("\nFATAL ERROR: Incompatible ratios. \n [%] \n [%]\n", T, U); | |
return false, msg; | |
} | |
// Compute new exponents | |
// If exponent is 0 then reduce ratio to 0 | |
le := le1 + le2; | |
ln := ifx le == 0 then 0 else ln1|ln2; | |
ld := ifx le == 0 then 0 else ld1|ld2; | |
me := me1 + me2; | |
mn := ifx me == 0 then 0 else mn1|mn2; | |
md := ifx me == 0 then 0 else md1|md2; | |
te := te1 + te2; | |
tn := ifx te == 0 then 0 else tn1|tn2; | |
td := ifx te == 0 then 0 else td1|td2; | |
// Compute final return type | |
RET = #dynamic_specialize SIQuantity( | |
DataType=datatype_a, | |
LengthNum=ln, LengthDenom=ld, LengthExp=le, | |
MassNum=mn, MassDenom=md, MassExp=me, | |
TimeNum=tn, TimeDenom=td, TimeExp=te); | |
return true; | |
} | |
{ | |
result : RET; | |
result.amount = a.amount * b.amount; | |
return result; | |
} | |
operator / :: (a:$T/SIQuantity, b:$U/SIQuantity) -> $RET | |
#modify { | |
ti1 := cast(*Type_Info_Struct)(T); | |
datatype_a := find_param(ti1, "DataType", Type); | |
ln1 := find_param(ti1, "LengthNum", int); | |
ld1 := find_param(ti1, "LengthDenom", int); | |
le1 := find_param(ti1, "LengthExp", int); | |
mn1 := find_param(ti1, "MassNum", int); | |
md1 := find_param(ti1, "MassDenom", int); | |
me1 := find_param(ti1, "MassExp", int); | |
tn1 := find_param(ti1, "TimeNum", int); | |
td1 := find_param(ti1, "TimeDenom", int); | |
te1 := find_param(ti1, "TimeExp", int); | |
ti2 := cast(*Type_Info_Struct)(U); | |
datatype_b := find_param(ti2, "DataType", Type); | |
ln2 := find_param(ti2, "LengthNum", int); | |
ld2 := find_param(ti2, "LengthDenom", int); | |
le2 := find_param(ti2, "LengthExp", int); | |
mn2 := find_param(ti2, "MassNum", int); | |
md2 := find_param(ti2, "MassDenom", int); | |
me2 := find_param(ti2, "MassExp", int); | |
tn2 := find_param(ti2, "TimeNum", int); | |
td2 := find_param(ti2, "TimeDenom", int); | |
te2 := find_param(ti2, "TimeExp", int); | |
if datatype_a != datatype_b { | |
msg := tprint("\nFATAL ERROR: Quantities must have same DataType.\n T: [%]\n U: [%]", T, U); | |
return false, msg; | |
} | |
// Ratios must be same OR one must be zero | |
ln_ok := ln1 == ln2 || ln1 == 0 || ln2 == 0; | |
ld_ok := ld1 == ld2 || ld1 == 0 || ld2 == 0; | |
mn_ok := mn1 == mn2 || mn1 == 0 || mn2 == 0; | |
md_ok := md1 == md2 || md1 == 0 || md2 == 0; | |
tn_ok := tn1 == tn2 || tn1 == 0 || tn2 == 0; | |
td_ok := td1 == td2 || td1 == 0 || td2 == 0; | |
if !ln_ok || !ld_ok || !mn_ok || !md_ok || !tn_ok || !td_ok | |
{ | |
msg := tprint("\nFATAL ERROR: Incompatible ratios. \n[%] \n[%]\n", T, U); | |
return false, msg; | |
} | |
// Compute new exponents | |
// If exponent is 0 then reduce ratio to 0 | |
le := le1 - le2; | |
ln := ifx le == 0 then 0 else ln1|ln2; | |
ld := ifx le == 0 then 0 else ld1|ld2; | |
me := me1 - me2; | |
mn := ifx me == 0 then 0 else mn1|mn2; | |
md := ifx me == 0 then 0 else md1|md2; | |
te := te1 - te2; | |
tn := ifx te == 0 then 0 else tn1|tn2; | |
td := ifx te == 0 then 0 else td1|td2; | |
RET = #dynamic_specialize SIQuantity( | |
DataType=datatype_a, | |
LengthNum=ln, LengthDenom=ld, LengthExp=le, | |
MassNum=mn, MassDenom=md, MassExp=me, | |
TimeNum=tn, TimeDenom=td, TimeExp=te); | |
return true; | |
} | |
{ | |
result : RET; | |
result.amount = a.amount / b.amount; | |
return result; | |
} | |
// Quantities can always be multiplied by a pure number | |
operator * :: (a: $T/SIQuantity, b: T.DataType) -> T #symmetric { | |
result : T; | |
result.amount = a.amount * b; | |
return result; | |
} | |
operator / :: (a: $T/SIQuantity, b: T.DataType) -> T { | |
result : T; | |
result.amount = a.amount / b; | |
return result; | |
} | |
sqrt :: (a:$T/SIQuantity) -> $RET | |
#modify { | |
ti1 := cast(*Type_Info_Struct)(T); | |
datatype := find_param(ti1, "DataType", Type); | |
ln1 := find_param(ti1, "LengthNum", int); | |
ld1 := find_param(ti1, "LengthDenom", int); | |
le1 := find_param(ti1, "LengthExp", int); | |
mn1 := find_param(ti1, "MassNum", int); | |
md1 := find_param(ti1, "MassDenom", int); | |
me1 := find_param(ti1, "MassExp", int); | |
tn1 := find_param(ti1, "TimeNum", int); | |
td1 := find_param(ti1, "TimeDenom", int); | |
te1 := find_param(ti1, "TimeExp", int); | |
// Exponents must be divisible by two | |
if le1%2 != 0 || me1 %2 != 0 || te1%2 != 0 { | |
print("\nFATAL ERROR: Can not sqrt non-even exponents. \n[%]\n", T); | |
return false; | |
} | |
// Create new exponents | |
le := le1 / 2; | |
me := me1 / 2; | |
te := te1 / 2; | |
RET = #dynamic_specialize SIQuantity( | |
DataType=datatype, | |
LengthNum=ln1, LengthDenom=ld1, LengthExp=le, | |
MassNum=mn1, MassDenom=md1, MassExp=me, | |
TimeNum=tn1, TimeDenom=td1, TimeExp=te); | |
return true; | |
} | |
{ | |
result : RET; | |
result.amount = sqrt(a.amount); | |
return result; | |
} | |
calc_ballistic_range :: ( | |
speed: MetersPerSecond(float), | |
gravity: MetersPerSecond2(float), | |
initial_height: Meters(float) | |
) -> Meters(float) | |
{ | |
assert(speed.amount > 0.0); | |
assert(gravity.amount > 0.0); | |
assert(initial_height.amount >= 0.0); | |
d2r : float: cast(float)0.01745329252; | |
angle : float: 45.0 * d2r; | |
ang_cos := cos(angle); | |
ang_sin := sin(angle); | |
range : Meters(float) = (speed * ang_cos / gravity) | |
* (speed * ang_sin + sqrt(speed * speed * ang_sin * ang_sin + 2.0 * gravity * initial_height)); | |
return range; | |
} | |
calc_ballistic_range2 :: ( | |
speed: $T/SIQuantity, | |
gravity: $U/SIQuantity, | |
initial_height: $V/SIQuantity | |
) -> $R | |
#modify { | |
// Assume return type uses the numerator/denominator of speed | |
// If gravity/height have different num/denom there will be a compile error | |
ti := cast(*Type_Info_Struct)(T); | |
dt := find_param(ti, "DataType", Type); | |
ln := find_param(ti, "LengthNum", int); | |
ld := find_param(ti, "LengthDenom", int); | |
R = #dynamic_specialize SIQuantity( | |
DataType=dt, | |
LengthNum=ln, LengthDenom=ld, LengthExp=1, | |
MassNum=0, MassDenom=0, MassExp=0, | |
TimeNum=0, TimeDenom=0, TimeExp=0); | |
return true; | |
} | |
{ | |
// Make sure units are correct | |
#assert T.LengthExp == 1 && T.MassExp == 0 && T.TimeExp == -1; | |
#assert U.LengthExp == 1 && U.MassExp == 0 && U.TimeExp == -2; | |
#assert V.LengthExp == 1 && V.MassExp == 0 && V.TimeExp == 0; | |
assert(speed.amount > 0.0); | |
assert(gravity.amount > 0.0); | |
assert(initial_height.amount >= 0.0); | |
d2r : speed.DataType: xx 0.01745329252; | |
angle :: 45.0 * d2r; | |
ang_cos := cos(angle); | |
ang_sin := sin(angle); | |
range := (speed * ang_cos / gravity) | |
* (speed * ang_sin + sqrt(speed * speed * ang_sin * ang_sin + 2.0 * gravity * initial_height)); | |
return range; | |
} | |
main :: () { | |
s := MetersPerSecond(float).{5}; | |
g := MetersPerSecond2(float).{9.8}; | |
h := Meters(float).{1}; | |
range := calc_ballistic_range2(s, g, h); | |
print("%\n", range); | |
s2 := KilometersPerSecond(float64).{.005}; | |
g2 := KilometersPerSecond2(float64).{.0098}; | |
h2 := Kilometers(float64).{0.001}; | |
range2 := calc_ballistic_range2(s2, g2, h2); | |
print("%\n", range2); | |
} | |
#import "Basic"; | |
#import "Math"; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment