//Note that basis conversion is the same as inverse rotation, //and basis deconversion is the same as rotation. public static tv2 ConvertBasis(ev2 source, ev2 basis1) => EEx.ResolveV2(source, basis1, // [ b1.x -b1.y ]^T [ x ] // [ b1.y b1.x ] [ y ] (s, b1) => ExUtils.V2( b1.x.Mul(s.x).Add(b1.y.Mul(s.y)), b1.x.Mul(s.y).Sub(b1.y.Mul(s.x)) ));
/// <summary> /// Wrap a position equation around a cylinder. /// </summary> /// <param name="radius">Radius of the cylinder</param> /// <param name="ang0">Starting angle offset (radians) on the cylinder. 0 = z-axis</param> /// <param name="maxWrap">Maximum angle value (radians) of the wrap. After this, the function will continue along the tangent. Starting offset not included. Absolute value tested.</param> /// <param name="axisOff">Offset angle (radians) of the axis of the cylinder from the y-axis</param> /// <param name="position">Position equation</param> /// <returns></returns> public static tv3 CylinderWrap(efloat radius, efloat ang0, efloat maxWrap, efloat axisOff, ev2 position) => EEx.Resolve(radius, ang0, axisOff, position, (r, a0, axis, _v2) => { var cs = new TExV2(); var xyd = new TExV2(); var v2 = new TExV2(_v2); var v3 = new TExV3(); var a = ExUtils.VFloat(); var aRem = ExUtils.VFloat(); var aMax = ExUtils.VFloat(); var a_cs = new TExV2(); var a0_cs = new TExV2(); return(Ex.Block(new[] { v3, cs, xyd, a, aRem, aMax, a_cs, a0_cs }, aMax.Is(maxWrap), cs.Is(CosSin(axis)), xyd.Is(ConvertBasis(v2, cs)), a.Is(xyd.x.Div(r)), aRem.Is(E0), Ex.IfThen(Abs(a).GT(aMax), Ex.Block( Ex.IfThen(a.LT0(), MulAssign(aMax, EN1)), aRem.Is(a.Sub(aMax)), a.Is(aMax) )), a0_cs.Is(CosSin(a0)), a_cs.Is(CosSin(a.Add(a0))), xyd.x.Is(r.Mul(a_cs.y.Sub(a0_cs.y).Add(aRem.Mul(a_cs.x)))), v3.Is(TP3(DeconvertBasis(xyd, cs))), v3.z.Is(r.Mul(a_cs.x.Sub(a0_cs.x).Sub(aRem.Mul(a_cs.y)))), v3 )); });
/// <summary> /// Periodize a value, /// "bouncing off" the endpoint instead of wrapping around. /// </summary> /// <example> /// <c> /// FSoftMod(X(), 4)(3.95) = 3.95 /// FSoftMod(X(), 4)(4.05) = 3.95 /// FSoftMod(X(), 4)(4.15) = 3.85 /// </c> /// </example> /// <param name="by">Period</param> /// <param name="x">Value</param> /// <returns></returns> public static tfloat SoftMod(efloat by, efloat x) => EEx.Resolve(by, _by => { var vd = VFloat(); return(Ex.Block(new[] { vd }, vd.Is(Mod(E2.Mul(_by), x)), Ex.Condition(vd.LT(_by), vd, E2.Mul(_by).Sub(vd)) )); });
/// <summary> /// Use Fermat's Little Theorem to reindex integers around a prime number mod, localized to the region /// [mod\*floor(index/mod), mod+mod\*floor(index/mod)]. /// </summary> public static tfloat RemapIndexLoop(efloat mod, efloat index) => EEx.Resolve(mod, index, (m, i) => { var rem = VFloat(); return(Ex.Block(new[] { rem }, rem.Is(Mod(m, i)), i.Sub(rem).Add(RemapIndex(m, rem)) )); });
/// <summary> /// Use this to draw "wings" where both go in opposite directions. /// <br/>Odd by: 0 is the center, [1,by/2-0.5] are one wing, and [by/2+0.5,by) are the other. /// <br/>Even by: [0, by/2) are one wing, [by/2, by) are the other. /// </summary> /// <example> /// <c> /// HNMod(X(), 9)(0) = HNMod(X(), 9)(9) = 0 /// HNMod(X(), 9)(1) = 1 /// HNMod(X(), 9)(5) = -1 /// HNMod(X(), 9)(8) = -4 /// HNMod(X(), 8)(0) = HNMod(X(), 8)(8) = 0.5 /// HNMod(X(), 8)(3) = 3.5 /// HNMod(X(), 8)(4) = -0.5 /// </c> /// </example> /// <param name="by">Period</param> /// <param name="x">Target function</param> /// <returns></returns> public static tfloat HNMod(tfloat by, tfloat x) => EEx.Resolve <float>(by.Div(E2), h => { var y = VFloat(); return(Ex.Block(new[] { y }, y.Is(Mod(h.Mul(E2), x)), Ex.Condition(y.LT(h), y.Add(Floor(h)).Add(E05).Sub(h), h.Sub(E05).Sub(y)) )); });
/// <summary> /// Lerp between two functions. The controller is not clamped. /// </summary> /// <param name="zeroBound">Lower bound for lerp controller</param> /// <param name="oneBound">Upper bound for lerp controller</param> /// <param name="controller">Lerp controller</param> /// <param name="f1">First function</param> /// <param name="f2">Second function</param> /// <returns></returns> public static TEx <T> LerpU <T>(efloat zeroBound, efloat oneBound, efloat controller, TEx <T> f1, TEx <T> f2) => EEx.Resolve(zeroBound, oneBound, controller, (z, o, c) => { var rc = VFloat(); return(Ex.Block(new[] { rc }, rc.Is(c.Sub(z).Div(o.Sub(z))), rc.Mul(f2).Add(rc.Complement().Mul(f1)) )); });
/// <summary> /// Lerp between two functions with smoothing applied to the controller. /// </summary> public static TEx <T> LerpSmooth <T>([LookupMethod] Func <tfloat, tfloat> smoother, efloat zeroBound, efloat oneBound, efloat controller, TEx <T> f1, TEx <T> f2) => EEx.Resolve(zeroBound, oneBound, controller, (z, o, c) => { var rc = VFloat(); return(Ex.Block(new[] { rc }, rc.Is(smoother(Clamp(z, o, c).Sub(z).Div(o.Sub(z)))), rc.Mul(f2).Add(rc.Complement().Mul(f1)) )); });
/// <summary> /// Note: requires that normalVec and planeVec are perpendicular. /// </summary> public static tv3 RotateInPlane(tfloat rot, tv3 normalVec, ev3 planeVec) => EEx.Resolve(planeVec, p => { var cs = new TExV2(); var cross = new TExV3(); return(Ex.Block(new ParameterExpression[] { cs, cross }, cs.Is(CosSinDeg(rot)), cross.Is(CrossProduct(normalVec, p)), cs.x.Mul(p).Add(cs.y.Mul(v3Mag(p).Div(v3Mag(cross)).Mul(cross))) )); });
public static tv3 FromSphere(tfloat radius, ev2 sphere) => radius.Mul(EEx.ResolveV2(sphere, s => { var cst = new TExV2(); var csp = new TExV2(); return(Ex.Block(new ParameterExpression[] { cst, csp }, cst.Is(CosSinDeg(s.x)), csp.Is(CosSinDeg(s.y)), V3(cst.x.Mul(csp.y), cst.y.Mul(csp.y), csp.x) )); }));
/// <summary> /// Normalize a vector. /// </summary> public static tv3 Norm3(ev3 v3) => EEx.ResolveV3(v3, xyz => { var mag = VFloat(); return(Ex.Block(new[] { mag }, mag.Is(v3Mag(xyz)), Ex.Condition(mag.GT(ExC(M.MAG_ERR)), xyz.Mul(E1.Div(mag)), xyz ) )); });
/// <summary> /// Normalize a vector. /// </summary> public static tv2 Norm(ev2 v2) => EEx.ResolveV2(v2, xy => { var mag = VFloat(); return(Ex.Block(new[] { mag }, mag.Is(Mag(xy)), Ex.Condition(mag.GT(ExC(M.MAG_ERR)), xy.Mul(E1.Div(mag)), xy ) )); });
public static tfloat EBounce2(tfloat x) => EEx.Resolve <float>((Ex)x, c => { var c1 = VFloat(); var c2 = VFloat(); return(Ex.Block(new[] { c1, c2 }, c1.Is(Min(E05, c.Mul(ExC(0.95f)))), c2.Is(Max(E0, c.Sub(E05))), c1.Add(c2).Add(ExC(0.4f).Mul( Sin(tau.Mul(c1)).Add(Sin(tau.Mul(c2))) )) )); }); //https://www.desmos.com/calculator/ix37mllnyp
/// <summary> /// Move a value in the range [-540, 540] to the range [-180, 180] by adding or subtracting 360. /// </summary> /// <param name="ang_rad"></param> /// <returns></returns> public static tfloat DegIntoRange(efloat ang_rad) => EEx.Resolve(ang_rad, a => Ex.Condition(a.GT(ExC(180f)), a.Sub(ExC(360f)), Ex.Condition(a.LT(ExC(-180f)), a.Add(ExC(360f)), a)));
/// <summary> /// Move a value in the range [-2pi, 2pi] to the range [0,2pi] by adding tau. /// </summary> /// <param name="ang_rad"></param> /// <returns></returns> public static tfloat RadToPos(efloat ang_rad) => EEx.Resolve(ang_rad, a => Ex.Condition(a.LT0(), a.Add(tau), a));
/// <summary> /// Move a value in the range [-2pi, 2pi] to the range [-2pi, 0] by subtracting tau. /// </summary> /// <param name="ang_rad"></param> /// <returns></returns> public static tfloat RadToNeg(efloat ang_rad) => EEx.Resolve(ang_rad, a => Ex.Condition(a.GT0(), a.Sub(tau), a));
/// <summary> /// Move a value in the range [-3pi, 3pi] to the range [-pi, pi] by adding or subtracting tau. /// </summary> /// <param name="ang_rad"></param> /// <returns></returns> public static tfloat RadIntoRange(efloat ang_rad) => EEx.Resolve(ang_rad, a => Ex.Condition(a.GT(pi), a.Sub(tau), Ex.Condition(a.LT(npi), a.Add(tau), a)));
/// <summary> /// If x's absolute value is greater than by, then return 0 instead. /// </summary> public static tfloat HighCut(tfloat by, efloat x) => EEx.Resolve(x, _x => Ex.Condition(Abs(_x).GT(by), E0, _x));
/// <summary> /// Limit a value x to have absolute value leq by. /// </summary> /// <param name="by">Positive number for absolute value comparison</param> /// <param name="x">Number to be limited</param> /// <returns></returns> public static tfloat Limit(efloat by, efloat x) => EEx.Resolve(by, x, (_by, _x) => Ex.Condition(_x.GT0(), Min(_x, _by), Max(_x, Ex.Negate(_by))));
/// <summary> /// Of two numbers, return the one with the larger absolute value. /// Not well-defined when x1 = -x2. /// </summary> public static tfloat MaxA(efloat x1, efloat x2) => EEx.Resolve(x1, x2, (a, b) => Ex.Condition(Abs(a).GT(Abs(b)), a, b));
/// <summary> /// = Floor(ex / block) * block /// </summary> public static tfloat BlockFloor(efloat block, tfloat ex) => EEx.Resolve(block, b => Floor(ex.Div(b)).Mul(b));
/// <summary> /// = Round(ex / block) * block /// </summary> public static tfloat BlockRound(efloat block, tfloat ex) => EEx.Resolve(block, b => Round(ex.Div(b)).Mul(b));
public static tfloat NPow(efloat bas, efloat exp) => EEx.Resolve(bas, exp, (x, y) => Ex.Condition(Ex.LessThan(x, E0), Ex.Negate(Pow(Ex.Negate(x), y)), Pow(x, y)));
/// <summary> /// Derive a V2RV2 from two vectors and a float. /// </summary> /// <param name="nrot">Nonrotational x,y</param> /// <param name="rot">Rotational x,y</param> /// <param name="angle">Rotational angle (degrees)</param> /// <returns></returns> public static trv2 V2V2F(ev2 nrot, ev2 rot, tfloat angle) => EEx.Resolve(nrot, rot, (_nr, _r) => { var nr = new TExV2(_nr); var r = new TExV2(_r); return(VRV2(nr.x, nr.y, r.x, r.y, angle)); });
/// <summary> /// Return the angle in radians whose tangent is v2.y/v2.x. /// </summary> public static tfloat ATanR(ev2 f) => EEx.Resolve(f, v2 => { var tv2 = new TExV2(v2); return(_AtanYX.Of(tv2.y, tv2.x)); });
/// <summary> /// Returns the quantity x^2+y^2+z^2. /// </summary> public static tfloat SqrMag3(efloat x, efloat y, efloat z) => EEx.Resolve(x, y, z, (_x, _y, _z) => _x.Mul(_x).Add(_y.Mul(_y).Add(_z.Mul(_z))));
/// <summary> /// Returns the quantity x^2+y^2. /// </summary> public static tfloat SqrMag2(efloat x, efloat y) => EEx.Resolve(x, y, (_x, _y) => _x.Mul(_x).Add(_y.Mul(_y)));
/// <summary> /// Derive a V2RV2 from a nonrotational vector and a rotational vector3. /// </summary> /// <param name="nrot">Nonrotational component</param> /// <param name="rot">Rotational component (x,y,angle)</param> /// <returns></returns> public static trv2 V2V3(ev2 nrot, ev3 rot) => EEx.Resolve(nrot, rot, (_nr, _r) => { var nr = new TExV2(_nr); var r = new TExV3(_r); return(VRV2(nr.x, nr.y, r.x, r.y, r.z)); });
/// <summary> /// Get the square magnitude of a vector. /// </summary> public static tfloat SqrMag(ev2 v2) => EEx.ResolveV2(v2, xy => SqrMag2(xy.x, xy.y));
/// <summary> /// Derive a color from a vector3 containing R, G, B components and an separate alpha component. /// </summary> public static tv4 TP3A(tfloat a, ev3 tp) => EEx.ResolveV3(tp, v3 => ExUtils.V4(v3.x, v3.y, v3.z, a));
/// <summary> /// Get the square magnitude of a vector. /// </summary> public static tfloat v3SqrMag(ev3 v3) => EEx.ResolveV3(v3, xyz => SqrMag3(xyz.x, xyz.y, xyz.z));