more nodes

This commit is contained in:
Connor
2026-02-22 15:40:25 +09:00
parent 3d4453b9ea
commit 376442e95a
5 changed files with 532 additions and 1192 deletions

View File

@@ -167,4 +167,248 @@ TEST_SUITE("WorldGraph::Nodes") {
PositionYNode n;
CHECK(n.Evaluate(c, {}).AsInt() == -3);
}
// ── Min / Max / Clamp ─────────────────────────────────────────────────────
TEST_CASE("MinNode: returns smaller value") {
MinNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(3.0f), Value::MakeFloat(7.0f) }).AsFloat()
== doctest::Approx(3.0f));
CHECK(n.Evaluate(ctx, { Value::MakeFloat(7.0f), Value::MakeFloat(3.0f) }).AsFloat()
== doctest::Approx(3.0f));
}
TEST_CASE("MinNode: equal inputs") {
MinNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(5.0f), Value::MakeFloat(5.0f) }).AsFloat()
== doctest::Approx(5.0f));
}
TEST_CASE("MaxNode: returns larger value") {
MaxNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(3.0f), Value::MakeFloat(7.0f) }).AsFloat()
== doctest::Approx(7.0f));
CHECK(n.Evaluate(ctx, { Value::MakeFloat(7.0f), Value::MakeFloat(3.0f) }).AsFloat()
== doctest::Approx(7.0f));
}
TEST_CASE("MaxNode: equal inputs") {
MaxNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(5.0f), Value::MakeFloat(5.0f) }).AsFloat()
== doctest::Approx(5.0f));
}
TEST_CASE("ClampNode: value within range passes through") {
ClampNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(5.0f), Value::MakeFloat(0.0f), Value::MakeFloat(10.0f) }).AsFloat()
== doctest::Approx(5.0f));
}
TEST_CASE("ClampNode: value below min clamped to min") {
ClampNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(-5.0f), Value::MakeFloat(0.0f), Value::MakeFloat(10.0f) }).AsFloat()
== doctest::Approx(0.0f));
}
TEST_CASE("ClampNode: value above max clamped to max") {
ClampNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(15.0f), Value::MakeFloat(0.0f), Value::MakeFloat(10.0f) }).AsFloat()
== doctest::Approx(10.0f));
}
// ── Int control flow ──────────────────────────────────────────────────────
TEST_CASE("IntBranchNode: selects true branch") {
IntBranchNode n;
auto r = n.Evaluate(ctx, { Value::MakeBool(true),
Value::MakeInt(7),
Value::MakeInt(99) });
CHECK(r.type == Type::Int);
CHECK(r.AsInt() == 7);
}
TEST_CASE("IntBranchNode: selects false branch") {
IntBranchNode n;
auto r = n.Evaluate(ctx, { Value::MakeBool(false),
Value::MakeInt(7),
Value::MakeInt(99) });
CHECK(r.type == Type::Int);
CHECK(r.AsInt() == 99);
}
TEST_CASE("IntBranchNode: output type is Int") {
IntBranchNode n;
CHECK(n.GetOutputType() == Type::Int);
CHECK(n.GetInputTypes() == std::vector<Type>{ Type::Bool, Type::Int, Type::Int });
}
// ── Extended math ─────────────────────────────────────────────────────────
TEST_CASE("SqrtNode: normal value") {
SqrtNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(9.0f) }).AsFloat() == doctest::Approx(3.0f));
}
TEST_CASE("SqrtNode: zero") {
SqrtNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(0.0f) }).AsFloat() == doctest::Approx(0.0f));
}
TEST_CASE("SqrtNode: negative input clamped to zero") {
SqrtNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(-4.0f) }).AsFloat() == doctest::Approx(0.0f));
}
TEST_CASE("PowNode: a^b") {
PowNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(2.0f), Value::MakeFloat(10.0f) }).AsFloat()
== doctest::Approx(1024.0f));
}
TEST_CASE("PowNode: anything to the power 0 is 1") {
PowNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(99.0f), Value::MakeFloat(0.0f) }).AsFloat()
== doctest::Approx(1.0f));
}
TEST_CASE("SquareNode: positive") {
SquareNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(5.0f) }).AsFloat() == doctest::Approx(25.0f));
}
TEST_CASE("SquareNode: negative input gives positive result") {
SquareNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(-3.0f) }).AsFloat() == doctest::Approx(9.0f));
}
TEST_CASE("OneMinusNode: 1 - 0.25 == 0.75") {
OneMinusNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(0.25f) }).AsFloat() == doctest::Approx(0.75f));
}
TEST_CASE("OneMinusNode: 1 - 0 == 1") {
OneMinusNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(0.0f) }).AsFloat() == doctest::Approx(1.0f));
}
TEST_CASE("OneMinusNode: 1 - 1 == 0") {
OneMinusNode n;
CHECK(n.Evaluate(ctx, { Value::MakeFloat(1.0f) }).AsFloat() == doctest::Approx(0.0f));
}
// ── Extended boolean logic ────────────────────────────────────────────────
TEST_CASE("XorNode: truth table") {
XorNode n;
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(false) }).AsBool() == false);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(false) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(true) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(true) }).AsBool() == false);
}
TEST_CASE("NandNode: truth table") {
NandNode n;
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(false) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(false) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(true) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(true) }).AsBool() == false);
}
TEST_CASE("NorNode: truth table") {
NorNode n;
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(false) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(false) }).AsBool() == false);
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(true) }).AsBool() == false);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(true) }).AsBool() == false);
}
TEST_CASE("XnorNode: truth table") {
XnorNode n;
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(false) }).AsBool() == true);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(false) }).AsBool() == false);
CHECK(n.Evaluate(ctx, { Value::MakeBool(false), Value::MakeBool(true) }).AsBool() == false);
CHECK(n.Evaluate(ctx, { Value::MakeBool(true), Value::MakeBool(true) }).AsBool() == true);
}
// ── Noise nodes ───────────────────────────────────────────────────────────
TEST_CASE("PerlinNoiseNode: output is float in [-1, 1]") {
EvalContext c; c.worldX = 10; c.worldY = 20; c.seed = 42;
PerlinNoiseNode n(0.1f);
float v = n.Evaluate(c, {}).AsFloat();
CHECK(v >= -1.0f);
CHECK(v <= 1.0f);
}
TEST_CASE("PerlinNoiseNode: deterministic for same context") {
EvalContext c; c.worldX = 5; c.worldY = -3; c.seed = 123;
PerlinNoiseNode n(0.05f);
float v1 = n.Evaluate(c, {}).AsFloat();
float v2 = n.Evaluate(c, {}).AsFloat();
CHECK(v1 == doctest::Approx(v2));
}
TEST_CASE("PerlinNoiseNode: different positions give different values") {
EvalContext c1; c1.worldX = 0; c1.worldY = 0; c1.seed = 99;
EvalContext c2; c2.worldX = 50; c2.worldY = 50; c2.seed = 99;
PerlinNoiseNode n(0.1f);
CHECK(n.Evaluate(c1, {}).AsFloat() != doctest::Approx(n.Evaluate(c2, {}).AsFloat()));
}
TEST_CASE("SimplexNoiseNode: output is float in [-1, 1]") {
EvalContext c; c.worldX = 7; c.worldY = 13; c.seed = 1;
SimplexNoiseNode n(0.1f);
float v = n.Evaluate(c, {}).AsFloat();
CHECK(v >= -1.0f);
CHECK(v <= 1.0f);
}
TEST_CASE("SimplexNoiseNode: deterministic for same context") {
EvalContext c; c.worldX = 3; c.worldY = 8; c.seed = 77;
SimplexNoiseNode n(0.05f);
CHECK(n.Evaluate(c, {}).AsFloat() == doctest::Approx(n.Evaluate(c, {}).AsFloat()));
}
TEST_CASE("CellularNoiseNode: output is float in [-1, 1]") {
EvalContext c; c.worldX = 2; c.worldY = 9; c.seed = 500;
CellularNoiseNode n(0.1f);
float v = n.Evaluate(c, {}).AsFloat();
CHECK(v >= -1.0f);
CHECK(v <= 1.0f);
}
TEST_CASE("CellularNoiseNode: deterministic for same context") {
EvalContext c; c.worldX = 15; c.worldY = -7; c.seed = 256;
CellularNoiseNode n(0.08f);
CHECK(n.Evaluate(c, {}).AsFloat() == doctest::Approx(n.Evaluate(c, {}).AsFloat()));
}
TEST_CASE("ValueNoiseNode: output is float in [-1, 1]") {
EvalContext c; c.worldX = 4; c.worldY = 6; c.seed = 11;
ValueNoiseNode n(0.1f);
float v = n.Evaluate(c, {}).AsFloat();
CHECK(v >= -1.0f);
CHECK(v <= 1.0f);
}
TEST_CASE("ValueNoiseNode: deterministic for same context") {
EvalContext c; c.worldX = -2; c.worldY = 3; c.seed = 404;
ValueNoiseNode n(0.05f);
CHECK(n.Evaluate(c, {}).AsFloat() == doctest::Approx(n.Evaluate(c, {}).AsFloat()));
}
TEST_CASE("noise nodes: frequency affects output") {
EvalContext c; c.worldX = 100; c.worldY = 100; c.seed = 1;
PerlinNoiseNode lowFreq(0.001f);
PerlinNoiseNode highFreq(0.5f);
// Different frequencies must produce different values at the same position
CHECK(lowFreq.Evaluate(c, {}).AsFloat() != doctest::Approx(highFreq.Evaluate(c, {}).AsFloat()));
}
TEST_CASE("noise nodes: seed affects output") {
EvalContext c1; c1.worldX = 10; c1.worldY = 10; c1.seed = 1;
EvalContext c2; c2.worldX = 10; c2.worldY = 10; c2.seed = 2;
PerlinNoiseNode n(0.1f);
CHECK(n.Evaluate(c1, {}).AsFloat() != doctest::Approx(n.Evaluate(c2, {}).AsFloat()));
}
}