using System;
namespace Ashfall.Core
{
[Serializable]
public struct V2
{
public float x, y;
public V2(float x, float y) { this.x = x; this.y = y; }
public float Length => (float)Math.Sqrt(x * x + y * y);
public float SqrLength => x * x + y * y;
public bool Finite => MathEx.Finite(x) && MathEx.Finite(y);
public V2 Normalized => Length > .0001f ? this / Length : new V2();
public static V2 operator +(V2 a, V2 b) => new V2(a.x + b.x, a.y + b.y);
public static V2 operator -(V2 a, V2 b) => new V2(a.x - b.x, a.y - b.y);
public static V2 operator *(V2 a, float b) => new V2(a.x * b, a.y * b);
public static V2 operator /(V2 a, float b) => new V2(a.x / b, a.y / b);
public static float Distance(V2 a, V2 b) => (a - b).Length;
public static float Dot(V2 a, V2 b) => a.x * b.x + a.y * b.y;
public static V2 Lerp(V2 a, V2 b, float t) => a + (b - a) * MathEx.Clamp(t, 0, 1);
public V2 Rotate(float degrees)
{
double r = degrees * Math.PI / 180;
return new V2((float)(x * Math.Cos(r) - y * Math.Sin(r)),
(float)(x * Math.Sin(r) + y * Math.Cos(r)));
}
public override string ToString() => $"({x:0.00}, {y:0.00})";
}
[Serializable]
public struct Box2
{
public V2 center, size;
public Box2(V2 center, V2 size) { this.center = center; this.size = size; }
public bool Contains(V2 p, float inset = 0) =>
Math.Abs(p.x - center.x) <= size.x * .5f - inset &&
Math.Abs(p.y - center.y) <= size.y * .5f - inset;
public bool Overlaps(Box2 b, float gap = 0) =>
Math.Abs(center.x - b.center.x) < (size.x + b.size.x) * .5f + gap &&
Math.Abs(center.y - b.center.y) < (size.y + b.size.y) * .5f + gap;
public bool BlocksSegment(V2 a, V2 b, float padding = 0)
{
float near = 0, far = 1;
V2 d = b - a;
return Clip(a.x, d.x, center.x - size.x / 2 - padding, center.x + size.x / 2 + padding, ref near, ref far)
&& Clip(a.y, d.y, center.y - size.y / 2 - padding, center.y + size.y / 2 + padding, ref near, ref far);
}
private static bool Clip(float start, float direction, float min, float max, ref float near, ref float far)
{
if (Math.Abs(direction) < .00001f) return start >= min && start <= max;
float a = (min - start) / direction, b = (max - start) / direction;
if (a > b) { float t = a; a = b; b = t; }
near = Math.Max(near, a); far = Math.Min(far, b);
return near <= far;
}
}
public static class MathEx
{
public static bool Finite(float f) => !float.IsNaN(f) && !float.IsInfinity(f);
public static float Clamp(float f, float a, float b) => Math.Max(a, Math.Min(f, b));
public static float Approach(float value, float target, float amount) => value < target
? Math.Min(target, value + amount) : Math.Max(target, value - amount);
public static float Angle(V2 direction) => (float)(Math.Atan2(direction.y, direction.x) * 180 / Math.PI) - 90;
public static float DeltaAngle(float a, float b) => ((b - a + 540) % 360 + 360) % 360 - 180;
public static float NormalizeAngle(float value) => (value % 360 + 360) % 360;
}
public static class Projection
{
public const float GroundScale = .819152f;
public const float HeightScale = .573576f;
public static V2 ToScreen(V2 ground, float height = 0) =>
new V2(ground.x, ground.y * GroundScale + height * HeightScale);
public static V2 ToGround(V2 screen, float height = 0) =>
new V2(screen.x, (screen.y - height * HeightScale) / GroundScale);
public static V2 InteriorToWorld(V2 local, V2 origin, float heading) => origin + local.Rotate(heading);
public static V2 WorldToInterior(V2 world, V2 origin, float heading) => (world - origin).Rotate(-heading);
}
public enum Species { Human, Brood }
public enum BroodForm { Worm, Adult }
public enum Family { Mechanical, Biological }
public enum SpaceKind { Exterior, Building, Vehicle }
public enum StationRole { Driver, Gunner }
public enum FlightMode { Ground, Low, Cruise }
public enum PieceKind { Floor, Wall, Door, Stairs, Research, Generator, Storage, Turret }
public enum FacilityKind { Research, Generator, Storage, Workshop, Medical }
public enum CommandKind { Interact, LeaveStation, InstallModule, Build, Dismantle, Research, Craft, Transfer, Cruise, ChangeAltitude, Repair, SwitchSpecies, Ability, Collect, Evolve }
[Serializable]
public class ActionResult
{
public bool success;
public string message;
public static ActionResult Ok(string text = "完成") => new ActionResult { success = true, message = text };
public static ActionResult Fail(string text) => new ActionResult { success = false, message = text };
}
}
using System;
namespace Ashfall.Core
{
[Serializable]
public struct V2
{
public float x, y;
public V2(float x, float y) { this.x = x; this.y = y; }
public float Length => (float)Math.Sqrt(x * x + y * y);
public float SqrLength => x * x + y * y;
public bool Finite => MathEx.Finite(x) && MathEx.Finite(y);
public V2 Normalized => Length > .0001f ? this / Length : new V2();
public static V2 operator +(V2 a, V2 b) => new V2(a.x + b.x, a.y + b.y);
public static V2 operator -(V2 a, V2 b) => new V2(a.x - b.x, a.y - b.y);
public static V2 operator *(V2 a, float b) => new V2(a.x * b, a.y * b);
public static V2 operator /(V2 a, float b) => new V2(a.x / b, a.y / b);
public static float Distance(V2 a, V2 b) => (a - b).Length;
public static float Dot(V2 a, V2 b) => a.x * b.x + a.y * b.y;
public static V2 Lerp(V2 a, V2 b, float t) => a + (b - a) * MathEx.Clamp(t, 0, 1);
public V2 Rotate(float degrees)
{
double r = degrees * Math.PI / 180;
return new V2((float)(x * Math.Cos(r) - y * Math.Sin(r)),
(float)(x * Math.Sin(r) + y * Math.Cos(r)));
}
public override string ToString() => $"({x:0.00}, {y:0.00})";
}
[Serializable]
public struct Box2
{
public V2 center, size;
public Box2(V2 center, V2 size) { this.center = center; this.size = size; }
public bool Contains(V2 p, float inset = 0) =>
Math.Abs(p.x - center.x) <= size.x * .5f - inset &&
Math.Abs(p.y - center.y) <= size.y * .5f - inset;
public bool Overlaps(Box2 b, float gap = 0) =>
Math.Abs(center.x - b.center.x) < (size.x + b.size.x) * .5f + gap &&
Math.Abs(center.y - b.center.y) < (size.y + b.size.y) * .5f + gap;
public bool BlocksSegment(V2 a, V2 b, float padding = 0)
{
float near = 0, far = 1;
V2 d = b - a;
return Clip(a.x, d.x, center.x - size.x / 2 - padding, center.x + size.x / 2 + padding, ref near, ref far)
&& Clip(a.y, d.y, center.y - size.y / 2 - padding, center.y + size.y / 2 + padding, ref near, ref far);
}
private static bool Clip(float start, float direction, float min, float max, ref float near, ref float far)
{
if (Math.Abs(direction) < .00001f) return start >= min && start <= max;
float a = (min - start) / direction, b = (max - start) / direction;
if (a > b) { float t = a; a = b; b = t; }
near = Math.Max(near, a); far = Math.Min(far, b);
return near <= far;
}
}
public static class MathEx
{
public static bool Finite(float f) => !float.IsNaN(f) && !float.IsInfinity(f);
public static float Clamp(float f, float a, float b) => Math.Max(a, Math.Min(f, b));
public static float Approach(float value, float target, float amount) => value < target
? Math.Min(target, value + amount) : Math.Max(target, value - amount);
public static float Angle(V2 direction) => (float)(Math.Atan2(direction.y, direction.x) * 180 / Math.PI) - 90;
public static float DeltaAngle(float a, float b) => ((b - a + 540) % 360 + 360) % 360 - 180;
public static float NormalizeAngle(float value) => (value % 360 + 360) % 360;
}
public static class Projection
{
public const float GroundScale = .819152f;
public const float HeightScale = .573576f;
public static V2 ToScreen(V2 ground, float height = 0) =>
new V2(ground.x, ground.y * GroundScale + height * HeightScale);
public static V2 ToGround(V2 screen, float height = 0) =>
new V2(screen.x, (screen.y - height * HeightScale) / GroundScale);
public static V2 InteriorToWorld(V2 local, V2 origin, float heading) => origin + local.Rotate(heading);
public static V2 WorldToInterior(V2 world, V2 origin, float heading) => (world - origin).Rotate(-heading);
}
public enum Species { Human, Brood }
public enum BroodForm { Worm, Adult }
public enum Family { Mechanical, Biological }
public enum SpaceKind { Exterior, Building, Vehicle }
public enum StationRole { Driver, Gunner }
public enum FlightMode { Ground, Low, Cruise }
public enum PieceKind { Floor, Wall, Door, Stairs, Research, Generator, Storage, Turret }
public enum FacilityKind { Research, Generator, Storage, Workshop, Medical }
public enum CommandKind { Interact, LeaveStation, InstallModule, Build, Dismantle, Research, Craft, Transfer, Cruise, ChangeAltitude, Repair, SwitchSpecies, Ability, Collect, Evolve }
[Serializable]
public class ActionResult
{
public bool success;
public string message;
public static ActionResult Ok(string text = "完成") => new ActionResult { success = true, message = text };
public static ActionResult Fail(string text) => new ActionResult { success = false, message = text };
}
}