diff --git a/sjplayer/src/sjplayer/SquareElement.d b/sjplayer/src/sjplayer/SquareElement.d index 9b001f3..0b2a517 100644 --- a/sjplayer/src/sjplayer/SquareElement.d +++ b/sjplayer/src/sjplayer/SquareElement.d @@ -1,10 +1,14 @@ /// License: MIT module sjplayer.SquareElement; +import std.algorithm, + std.typecons; + import gl4d; import sjplayer.ElementDrawer, - sjplayer.ElementInterface; + sjplayer.ElementInterface, + sjplayer.util.linalg; /// class SquareElement : ElementInterface { @@ -30,7 +34,33 @@ class SquareElement : ElementInterface { } override DamageCalculationResult CalculateDamage(vec2 p1, vec2 p2) const { - return DamageCalculationResult(0, 0); // TODO: + if (!alive) return DamageCalculationResult(0, 0); + + const m = matrix.inverse; + const a = (m * vec3(p1, 1)).xy; + const b = (m * vec3(p2, 1)).xy; + + if ((-1 <= a.x && a.x <= 1 && -1 <= a.y && a.y <= 1) || + (-1 <= b.x && b.x <= 1 && -1 <= b.y && b.y <= 1)) { + return DamageCalculationResult(damage, 0); + } + + enum edges = [ + tuple(vec2(-1, 1), vec2(-1, -1)), + tuple(vec2(-1, -1), vec2( 1, -1)), + tuple(vec2( 1, -1), vec2( 1, 1)), + tuple(vec2( 1, 1), vec2(-1, 1)), + ]; + float[4] distances; + static foreach (i, edge; edges) { + distances[i] = CalculateDistance2LineSegment(edge[0], edge[1], a, b); + } + const min_distance = distances[].minElement; + + if (min_distance == 0) { + return DamageCalculationResult(damage, 0); + } + return DamageCalculationResult(0, 1-(min_distance-1).clamp(0f, 1f)); } /// diff --git a/sjplayer/src/sjplayer/util/linalg.d b/sjplayer/src/sjplayer/util/linalg.d index 83196ac..dad0398 100644 --- a/sjplayer/src/sjplayer/util/linalg.d +++ b/sjplayer/src/sjplayer/util/linalg.d @@ -30,3 +30,50 @@ unittest { assert(CalculateDistanceOriginAndLineSegment( vec2(-2, 1), vec2(-1, 1)).approxEqual(sqrt(2f))); } + +/// +float CalculateDistance2LineSegment(vec2 a1, vec2 b1, vec2 a2, vec2 b2) { + float cross2(vec2 v1, vec2 v2) { + return v1.x * v2.y - v1.y * v2.x; + } + + if (a1 == b1) return CalculateDistanceOriginAndLineSegment(a2 - a1, b2 - a1); + if (a2 == b2) return CalculateDistanceOriginAndLineSegment(a1 - a2, b1 - a2); + + const a1_to_b1 = b1 - a1; + const a2_to_b2 = b2 - a2; + + if (cross2(a1_to_b1, a2_to_b2) == 0) { + return CalculateDistanceOriginAndLineSegment(a2 - a1, b2 - a1); + } + + const a1_to_a2 = a2 - a1; + const a1_to_b2 = b2 - a1; + const a2_to_a1 = a1 - a2; + const a2_to_b1 = b1 - a2; + + if (cross2(a1_to_a2, a1_to_b1) * cross2(a1_to_b2, a1_to_b1) < 0 && + cross2(a2_to_a1, a2_to_b2) * cross2(a2_to_b1, a2_to_b2) < 0) { + return 0; + } + + const b1_to_a2 = a2 - b1; + const b1_to_b2 = b2 - b1; + return min( + CalculateDistanceOriginAndLineSegment(a1_to_a2, a1_to_b2), + CalculateDistanceOriginAndLineSegment(b1_to_a2, b1_to_b2) + ); +} +/// +unittest { + import std; + assert(CalculateDistance2LineSegment( + vec2(1, 0), vec2(1, 0), vec2(0, 1), vec2(0, -1)).approxEqual(1f)); + + assert(CalculateDistance2LineSegment( + vec2(-1, 0), vec2(1, 0), vec2(0, 1), vec2(0, -1)).approxEqual(0f)); + assert(CalculateDistance2LineSegment( + vec2(-1, 0), vec2(1, 0), vec2(1, 1), vec2(1, -1)).approxEqual(0f)); + assert(CalculateDistance2LineSegment( + vec2(-1, 0), vec2(1, 0), vec2(2, 1), vec2(2, -1)).approxEqual(1f)); +}