Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion h2d/col/Segments.hx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,20 @@ abstract Segments(Array<Segment>) from Array<Segment> to Array<Segment> {
if( s.side(p) < 0 )
return false;
} else {
throw "TODO";
// Ray-casting (winding number) algorithm for arbitrary polygons.
// Counts intersections of a horizontal ray starting from point p and going right.
var w = 0;
for( s in segments ) {
var startY = s.y;
var endY = s.y + s.dy;
if( endY <= p.y ) {
if( startY > p.y && s.side(p) < 0 )
w++;
} else if( startY <= p.y && s.side(p) > 0 ) {
w--;
}
}
return w != 0;
}
return true;
}
Expand Down
4 changes: 4 additions & 0 deletions test.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-cp test
-main test.TestRunner
-lib heaps
--interp
44 changes: 44 additions & 0 deletions test/Assert.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package test;

class Assert {
public static var failures:Int = 0;
public static var successes:Int = 0;

public static function isTrue(value:Bool, ?pos:haxe.PosInfos) {
if (!value) {
failures++;
trace("FAIL: Expected true, got false at " + pos.fileName + ":" + pos.lineNumber);
} else {
successes++;
}
}

public static function isFalse(value:Bool, ?pos:haxe.PosInfos) {
if (value) {
failures++;
trace("FAIL: Expected false, got true at " + pos.fileName + ":" + pos.lineNumber);
} else {
successes++;
}
}

public static function equals<T>(expected:T, actual:T, ?pos:haxe.PosInfos) {
if (expected != actual) {
failures++;
trace("FAIL: Expected " + expected + ", got " + actual + " at " + pos.fileName + ":" + pos.lineNumber);
} else {
successes++;
}
}

public static function floatEquals(expected:Float, actual:Float, epsilon:Float = 1e-6, ?pos:haxe.PosInfos) {
var diff = expected - actual;
if (diff < 0) diff = -diff;
if (diff > epsilon) {
failures++;
trace("FAIL: Expected " + expected + ", got " + actual + " (diff " + diff + " > epsilon " + epsilon + ") at " + pos.fileName + ":" + pos.lineNumber);
} else {
successes++;
}
}
}
64 changes: 64 additions & 0 deletions test/TestMath.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package test;

class TestMath {
public function new() {}

public function run() {
testClamp();
testIsPOT();
testNextPOT();
testLerp();
testInverseLerp();
testFmt();
}

function testClamp() {
Assert.equals(5.0, hxd.Math.clamp(5.0, 0.0, 10.0));
Assert.equals(0.0, hxd.Math.clamp(-1.0, 0.0, 10.0));
Assert.equals(10.0, hxd.Math.clamp(15.0, 0.0, 10.0));
Assert.equals(0.5, hxd.Math.clamp(0.5)); // Default range: 0 to 1
Assert.equals(0.0, hxd.Math.clamp(-0.2));
Assert.equals(1.0, hxd.Math.clamp(1.5));
}

function testIsPOT() {
Assert.isTrue(hxd.Math.isPOT(1));
Assert.isTrue(hxd.Math.isPOT(2));
Assert.isTrue(hxd.Math.isPOT(4));
Assert.isTrue(hxd.Math.isPOT(1024));
Assert.isFalse(hxd.Math.isPOT(3));
Assert.isFalse(hxd.Math.isPOT(5));
Assert.isFalse(hxd.Math.isPOT(1000));
}

function testNextPOT() {
Assert.equals(0, hxd.Math.nextPOT(0));
Assert.equals(1, hxd.Math.nextPOT(1));
Assert.equals(2, hxd.Math.nextPOT(2));
Assert.equals(4, hxd.Math.nextPOT(3));
Assert.equals(4, hxd.Math.nextPOT(4));
Assert.equals(8, hxd.Math.nextPOT(5));
Assert.equals(1024, hxd.Math.nextPOT(1000));
}

function testLerp() {
Assert.floatEquals(5.0, hxd.Math.lerp(0.0, 10.0, 0.5));
Assert.floatEquals(0.0, hxd.Math.lerp(0.0, 10.0, 0.0));
Assert.floatEquals(10.0, hxd.Math.lerp(0.0, 10.0, 1.0));
Assert.floatEquals(2.5, hxd.Math.lerp(0.0, 10.0, 0.25));
}

function testInverseLerp() {
Assert.floatEquals(0.5, hxd.Math.inverseLerp(0.0, 10.0, 5.0));
Assert.floatEquals(0.0, hxd.Math.inverseLerp(0.0, 10.0, 0.0));
Assert.floatEquals(1.0, hxd.Math.inverseLerp(0.0, 10.0, 10.0));
Assert.floatEquals(0.25, hxd.Math.inverseLerp(0.0, 10.0, 2.5));
}

function testFmt() {
// fmt rounds to 4 significant digits (or standard forms)
Assert.floatEquals(1.234, hxd.Math.fmt(1.234));
Assert.floatEquals(1.234, hxd.Math.fmt(1.2343));
Assert.floatEquals(0.0, hxd.Math.fmt(1e-12));
}
}
25 changes: 25 additions & 0 deletions test/TestRunner.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package test;

class TestRunner {
public static function main() {
Sys.println("Starting test execution...");

var mathTests = new TestMath();
mathTests.run();

var segmentsTests = new TestSegments();
segmentsTests.run();

var shaderCacheTests = new TestShaderCache();
shaderCacheTests.run();

Sys.println("Successes: " + Assert.successes);
if (Assert.failures > 0) {
Sys.println("Failures: " + Assert.failures);
Sys.exit(1);
} else {
Sys.println("All tests passed!");
Sys.exit(0);
}
}
}
91 changes: 91 additions & 0 deletions test/TestSegments.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package test;

import h2d.col.Point;
import h2d.col.Segment;
import h2d.col.Segments;

class TestSegments {
public function new() {}

public function run() {
testConvexContains();
testConcaveContains();
testDistanceAndProject();
}

function testConvexContains() {
var p1 = new Point(0, 0);
var p2 = new Point(0, 10);
var p3 = new Point(10, 10);
var p4 = new Point(10, 0);

// For containsPoint(p, true) to succeed, the segments must be counter-clockwise
// so that the point is on the inside side of all segment axes (s.side(p) >= 0).
var ccw_s1 = new Segment(p1, p4);
var ccw_s2 = new Segment(p4, p3);
var ccw_s3 = new Segment(p3, p2);
var ccw_s4 = new Segment(p2, p1);

var ccw_segments:Segments = [ccw_s1, ccw_s2, ccw_s3, ccw_s4];

Assert.isTrue(ccw_segments.containsPoint(new Point(5, 5), true));
Assert.isFalse(ccw_segments.containsPoint(new Point(15, 5), true));
Assert.isFalse(ccw_segments.containsPoint(new Point(-5, 5), true));
}

function testConcaveContains() {
// Create an L-shaped concave polygon
// Vertices: (0,0) -> (10,0) -> (10,5) -> (5,5) -> (5,10) -> (0,10) -> (0,0)
var pts = [
new Point(0, 0),
new Point(10, 0),
new Point(10, 5),
new Point(5, 5),
new Point(5, 10),
new Point(0, 10)
];

var segmentsList = [];
var prev = pts[pts.length - 1];
for (p in pts) {
segmentsList.push(new Segment(prev, p));
prev = p;
}
var segments:Segments = segmentsList;

// Try containsPoint with isConvex = false.
// This should run our implementation without throwing "TODO".
try {
var inside1 = segments.containsPoint(new Point(2, 2), false); // inside
var inside2 = segments.containsPoint(new Point(8, 2), false); // inside
var inside3 = segments.containsPoint(new Point(8, 8), false); // outside (in the hollow of the L)
var inside4 = segments.containsPoint(new Point(2, 8), false); // inside
var outside = segments.containsPoint(new Point(12, 12), false); // outside

Assert.isTrue(inside1);
Assert.isTrue(inside2);
Assert.isFalse(inside3);
Assert.isTrue(inside4);
Assert.isFalse(outside);
} catch (e:Dynamic) {
Assert.isTrue(false);
trace("Exception in containsPoint(..., false): " + e);
}
}

function testDistanceAndProject() {
var p1 = new Point(0, 0);
var p2 = new Point(10, 0);
var s = new Segment(p1, p2);
var segments:Segments = [s];

// test distance
Assert.floatEquals(5.0, segments.distance(new Point(5, 5)));
Assert.floatEquals(25.0, segments.distanceSq(new Point(5, 5)));

// test project
var proj = segments.project(new Point(5, 5));
Assert.floatEquals(5.0, proj.x);
Assert.floatEquals(0.0, proj.y);
}
}
41 changes: 41 additions & 0 deletions test/TestShaderCache.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package test;

import h3d.impl.ShaderCache;
import haxe.io.Bytes;

class TestShaderCache {
public function new() {}

public function run() {
testShaderCacheInMemoryWithAllowSaveFalse();
}

function testShaderCacheInMemoryWithAllowSaveFalse() {
// Create a mock/temporary cache file path.
var cacheFile = "temp_shader_cache.data";
var cache = new ShaderCache(cacheFile);

// Turn off disk persistence.
cache.allowSave = false;
cache.keepSource = true;

var shaderSource = "
@input float4 position;
void main() {
output.position = position;
}
";
var compiledBinary = Bytes.ofString("MOCK_COMPILED_SHADER_BINARY");

// Save the compiled shader. It should cache in-memory.
cache.saveCompiledShader(shaderSource, compiledBinary);

// Try to resolve the shader binary from the cache.
var resolved = cache.resolveShaderBinary(shaderSource);

Assert.isTrue(resolved != null);
if (resolved != null) {
Assert.equals(compiledBinary.toString(), resolved.toString());
}
}
}