From 47b4377c947f490a0d48f43042efd2b98adbde1e Mon Sep 17 00:00:00 2001 From: Enderek Date: Thu, 1 Jan 2026 12:33:16 +0100 Subject: [PATCH 1/5] add: initial implementation of ParametricLine --- misc/math.as | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 misc/math.as diff --git a/misc/math.as b/misc/math.as new file mode 100644 index 0000000..cf885df --- /dev/null +++ b/misc/math.as @@ -0,0 +1,117 @@ +// -------------------------------- +// Purpose: Provide convienience functions/objects that aid with math. +// -------------------------------- + +const double epsilon_ = 0.0005; + +/* Represents a 3D parametric line in space. +x = vx * t + point_x +y = vy * t + point_y, t belongs to Real +z = vz * t + point_z + +This object is immutable, if you want a different line, you need to create a new object for it. +*/ +class ParametricLine { + protected double vx, vy, vz; + protected Vector point; + + // Create a line from a vector pointing a direction and a point that belongs to this line. + ParametricLine(const Vector dir, const Vector point) { + this.vx = dir[0]; + this.vy = dir[1]; + this.vz = dir[2]; + + this.point = point; + } + + // Create a line from raw dir vector components, for double precision, and a point that belongs to this line. + ParametricLine(const double vx, const double vy, const double vz, const Vector point) { + this.vx = vx; + this.vy = vy; + this.vz = vz; + this.point = point; + } + + double GetVX() const { + return this.vx; + } + + double GetVY() const { + return this.vy; + } + + double GetVZ() const { + return this.vz; + } + + // Returns if this point belong to the line (or is extremely close). Pass epsilon to set the accuracy, default is 0.00005 + bool PointBelongsToLine(const Vector point, const double epsilon = epsilon_) { + // Just check if we can solve this system of equations by using one t value + double t = (point[0] - this.point[0]) / this.vx; + + return ( + abs(point[1] - this.vy * t - this.point[1]) < epsilon && + abs(point[2] - this.vz * t - this.point[2]) < epsilon + ); + } + + // Get a point at specific t + Vector GetPointAtT(const double t) { + return Vector(this.vx * t, this.vy * t, this.vz * t) + point; + } + + // Calculate the intersection point (if exists) of two parametric lines. + // Vector parameter is the result (&out), function returns bool to indicate if the intersection exists. + bool LineIntersection(const ParametricLine&in line, Vector&out intersection, const double epsilon = 0.0005) { + /* We're solving + v1x * t + p1x = v2x * s + p2x + v1y * t + p1y = v2y * s + p2y + v1z * t + p1z = v2z * s + p2z + + We know: v1, v2, p1, p2. We can calculate t and s and check if the last condition is true. + + t = (v2x * s + p2x - p1x) / v1x + s = (v1y(v2x * s + p2x - p1x) / v1x + p1y - p2y) / v2y + */ + double mi = this.vx / this.vy; + Vector tp = line.GetPointAtT(0); // Their point; doesn't really matter which one we get, but 0 gives us the original one + + double s = mi * (tp[1] - this.point[1]) + this.point[0]; + s /= line.GetVX() - (line.GetVY() * mi); + + double t = (line.GetVX() * s + tp[0] - this.point[0] ) / this.vx; + + if (abs(this.vz * t + this.point[2] - line.GetVZ() * s - tp[2]) < epsilon) { + intersection = ( this.GetPointAtT(t) + line.GetPointAtT(s) ) / 2; //This will give us the best approximation in case we are not "perfectly" intersecting (but are very close) + return true; + } else { + return false; + } + } + + // Perform an orthogonal projection onto this line + Vector OrthogonalProjection(const Vector point) { + if (this.PointBelongsToLine(point)) { // If the point belongs to the line, it is it's own projection of itself + return point; + } + + // P' (orthogonal projection of P) = [a, b, c] + // Vec(PP') = [a - px, b - py, c - pz] + // Vec(PP') dot [vx, vy, vz] == 0, these vectors must be perpendicular + // We get: vx(a-px) + vy(b-py), vz(c-pz) = 0 + + // Additionally, P' must belong to us p0 = this.point + // our final equation is, we want to get t + // vx(vx * t + p0x - px) + vy(vy * t + p0y - py) + vz(vz * t + p0z - pz) = 0 + // t(vx^2 + vy^2 + vz^2) = vx(px - p0x) + vy(py - p0y) + vz(pz - p0z) + + double t = ( this.vx * (point[0] - this.point[0]) + this.vy * (point[1] - this.point[1]) + this.vz * (point[2] - this.point[2]) ) / (this.vx * this.vx + this.vy * this.vy + this.vz * this.vz); + + return GetPointAtT(t); + } + + // Calculate the distance from this point to this parametric line. + double GetDistance(const Vector point) { + return this.point.Cross(point - this.point).Length() / point.Length(); + } +} \ No newline at end of file From a81e47f35fa1d3ee48c42505fb5e9cd3bbbfcf6b Mon Sep 17 00:00:00 2001 From: Enderek Date: Thu, 1 Jan 2026 21:13:13 +0100 Subject: [PATCH 2/5] change: Initial implementation of ParametricLine completed --- misc/math.as | 42 +++++++++++++++++++++++------------- tests/test_ParametricLine.as | 35 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 tests/test_ParametricLine.as diff --git a/misc/math.as b/misc/math.as index cf885df..90fd554 100644 --- a/misc/math.as +++ b/misc/math.as @@ -16,7 +16,7 @@ class ParametricLine { protected Vector point; // Create a line from a vector pointing a direction and a point that belongs to this line. - ParametricLine(const Vector dir, const Vector point) { + ParametricLine(const Vector&in dir, const Vector point) { this.vx = dir[0]; this.vy = dir[1]; this.vz = dir[2]; @@ -45,24 +45,36 @@ class ParametricLine { } // Returns if this point belong to the line (or is extremely close). Pass epsilon to set the accuracy, default is 0.00005 - bool PointBelongsToLine(const Vector point, const double epsilon = epsilon_) { - // Just check if we can solve this system of equations by using one t value - double t = (point[0] - this.point[0]) / this.vx; - - return ( - abs(point[1] - this.vy * t - this.point[1]) < epsilon && - abs(point[2] - this.vz * t - this.point[2]) < epsilon - ); + bool PointBelongsToLine(const Vector&in point, const double epsilon = epsilon_) const { + // We just need to check if we can get a t. + double _; + return this.GetParameterForPoint(point, _, epsilon); } // Get a point at specific t - Vector GetPointAtT(const double t) { + Vector GetPointAtT(const double t) const { return Vector(this.vx * t, this.vy * t, this.vz * t) + point; } + // Get the parameter value for this point, if this point belongs to this line. + bool GetParameterForPoint(const Vector&in point, double&out t_out, const double epsilon = epsilon_) const { + + double t = (point[0] - this.point[0]) / this.vx; + + if ( + abs(point[1] - this.vy * t - this.point[1]) < epsilon && + abs(point[2] - this.vz * t - this.point[2]) < epsilon + ) { + t_out = t; + return true; + } else { + return false; + } + } + // Calculate the intersection point (if exists) of two parametric lines. // Vector parameter is the result (&out), function returns bool to indicate if the intersection exists. - bool LineIntersection(const ParametricLine&in line, Vector&out intersection, const double epsilon = 0.0005) { + bool LineIntersection(const ParametricLine&in line, Vector&out intersection, const double epsilon = 0.0005) const { /* We're solving v1x * t + p1x = v2x * s + p2x v1y * t + p1y = v2y * s + p2y @@ -77,7 +89,7 @@ class ParametricLine { Vector tp = line.GetPointAtT(0); // Their point; doesn't really matter which one we get, but 0 gives us the original one double s = mi * (tp[1] - this.point[1]) + this.point[0]; - s /= line.GetVX() - (line.GetVY() * mi); + s /= (line.GetVY() * mi) - line.GetVX(); double t = (line.GetVX() * s + tp[0] - this.point[0] ) / this.vx; @@ -90,7 +102,7 @@ class ParametricLine { } // Perform an orthogonal projection onto this line - Vector OrthogonalProjection(const Vector point) { + Vector OrthogonalProjection(const Vector&in point) const { if (this.PointBelongsToLine(point)) { // If the point belongs to the line, it is it's own projection of itself return point; } @@ -98,7 +110,7 @@ class ParametricLine { // P' (orthogonal projection of P) = [a, b, c] // Vec(PP') = [a - px, b - py, c - pz] // Vec(PP') dot [vx, vy, vz] == 0, these vectors must be perpendicular - // We get: vx(a-px) + vy(b-py), vz(c-pz) = 0 + // We get: vx(a-px) + vy(b-py) + vz(c-pz) = 0 // Additionally, P' must belong to us p0 = this.point // our final equation is, we want to get t @@ -111,7 +123,7 @@ class ParametricLine { } // Calculate the distance from this point to this parametric line. - double GetDistance(const Vector point) { + double GetDistance(const Vector&in point) const { return this.point.Cross(point - this.point).Length() / point.Length(); } } \ No newline at end of file diff --git a/tests/test_ParametricLine.as b/tests/test_ParametricLine.as new file mode 100644 index 0000000..72c4d62 --- /dev/null +++ b/tests/test_ParametricLine.as @@ -0,0 +1,35 @@ +// -------------------------------- +// Purpose: Test the parametric line class in math.as +// -------------------------------- + + +#include "../misc/math.as" +#include "../misc/logger.as" + +[ServerCommand("as_pline_test", "")] +void test_parametricline(const CommandArgs@ args) { + Logger logger("PLine_logger"); + + ParametricLine pline1(Vector(1, 2, -1), Vector(1, -3, 5)); + ParametricLine pline2(Vector(2, 1, -1), Vector(6, 1, 2)); + + Vector intersection; + + bool did_intersect = pline1.LineIntersection(pline2, intersection); + + //Intersection should occur at (2, -1, 4) + logger.Info("Intersection did occur: " + did_intersect); + logger.Info("Intersection coordinates: " + intersection[0] + " " + intersection[1] + " " + intersection[2]); + + + logger.Info("Checking if point (3, 1, 3) belongs to pline1 (it does): " + pline1.PointBelongsToLine(Vector(3, 1, 3))); + logger.Info("Checking if point (3, 0, 3) belongs to pline1 (it doesn't): " + pline1.PointBelongsToLine(Vector(3, 0, 3))); + + Vector projection = pline1.OrthogonalProjection(Vector(10, 10, 10)); + logger.Info("Performing orthogonal projection of point (10, 10, 10) onto pline1 (expected result is 6, 7, 0): (" + projection[0] + ", " + projection[1] + ", " + projection[2] + ")"); + + double t; + bool point_exists = pline1.GetParameterForPoint(projection, t); + logger.Info(" For the point up above, t = " + t); + +} \ No newline at end of file From 0dfd46ac7cef72c81b7c6331cb8d6498e4d21cdd Mon Sep 17 00:00:00 2001 From: Enderek Date: Thu, 1 Jan 2026 21:13:34 +0100 Subject: [PATCH 3/5] change: Update tests/load_all to include recent tests --- tests/load_all.as | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/load_all.as b/tests/load_all.as index 7e6d18d..2008416 100644 --- a/tests/load_all.as +++ b/tests/load_all.as @@ -2,4 +2,6 @@ // Purpose: Convenience object to load all tests at once. // -------------------------------- -#include "test_CuboidVolumeSimple.as" \ No newline at end of file +#include "test_logger.as" +#include "test_CuboidVolumeSimple.as" +#include "test_ParametricLine.as" \ No newline at end of file From cc1bdb4c70d4703cc8dc172ea9fb96a738a00b1b Mon Sep 17 00:00:00 2001 From: Enderek Date: Fri, 2 Jan 2026 11:12:00 +0100 Subject: [PATCH 4/5] change: Update comment, ParametricLine acts as immutable, but is still a reference type --- misc/math.as | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/math.as b/misc/math.as index 90fd554..a299585 100644 --- a/misc/math.as +++ b/misc/math.as @@ -9,7 +9,7 @@ x = vx * t + point_x y = vy * t + point_y, t belongs to Real z = vz * t + point_z -This object is immutable, if you want a different line, you need to create a new object for it. +This object acts as immutable, if you want a different line, you need to create a new object for it. */ class ParametricLine { protected double vx, vy, vz; From 45b48cb3e3b001f4dd4def5fa06b9eb5570c16f0 Mon Sep 17 00:00:00 2001 From: Enderek Date: Mon, 16 Feb 2026 11:58:15 +0100 Subject: [PATCH 5/5] change: Move math.as into math/ and separate into ParametricLine.as and constants.as --- misc/math.as => math/ParametricLine.as | 10 +++++----- math/constants.as | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) rename misc/math.as => math/ParametricLine.as (95%) create mode 100644 math/constants.as diff --git a/misc/math.as b/math/ParametricLine.as similarity index 95% rename from misc/math.as rename to math/ParametricLine.as index a299585..bdd8e84 100644 --- a/misc/math.as +++ b/math/ParametricLine.as @@ -1,8 +1,8 @@ // -------------------------------- -// Purpose: Provide convienience functions/objects that aid with math. +// Purpose: Represents a 3D parametric line in space. // -------------------------------- -const double epsilon_ = 0.0005; +#include "constants.as" /* Represents a 3D parametric line in space. x = vx * t + point_x @@ -45,7 +45,7 @@ class ParametricLine { } // Returns if this point belong to the line (or is extremely close). Pass epsilon to set the accuracy, default is 0.00005 - bool PointBelongsToLine(const Vector&in point, const double epsilon = epsilon_) const { + bool PointBelongsToLine(const Vector&in point, const double epsilon = EPSILON) const { // We just need to check if we can get a t. double _; return this.GetParameterForPoint(point, _, epsilon); @@ -57,7 +57,7 @@ class ParametricLine { } // Get the parameter value for this point, if this point belongs to this line. - bool GetParameterForPoint(const Vector&in point, double&out t_out, const double epsilon = epsilon_) const { + bool GetParameterForPoint(const Vector&in point, double&out t_out, const double epsilon = EPSILON) const { double t = (point[0] - this.point[0]) / this.vx; @@ -74,7 +74,7 @@ class ParametricLine { // Calculate the intersection point (if exists) of two parametric lines. // Vector parameter is the result (&out), function returns bool to indicate if the intersection exists. - bool LineIntersection(const ParametricLine&in line, Vector&out intersection, const double epsilon = 0.0005) const { + bool LineIntersection(const ParametricLine&in line, Vector&out intersection, const double epsilon = EPSILON) const { /* We're solving v1x * t + p1x = v2x * s + p2x v1y * t + p1y = v2y * s + p2y diff --git a/math/constants.as b/math/constants.as new file mode 100644 index 0000000..7d64d39 --- /dev/null +++ b/math/constants.as @@ -0,0 +1,8 @@ +// -------------------------------- +// Purpose: Provides various constants. +// -------------------------------- + +// Used for accuracy when comparing numbers in various functions. Numbers are equal if |a - b| < epsilon +const double EPSILON = 0.0005; + +const double PI = 3.14159265359; \ No newline at end of file