From 1402ca7d6c2bb0cea2cf52a4ecce7b809ebf31b1 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 28 Oct 2026 10:31:32 +0100 Subject: Reject malformed spline counts and unsorted knots Co-Authored-By: Claude Haiku 5.5 --- generat3d/formula.py | 8 +++++++- tests/test_formula.py | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/generat3d/formula.py b/generat3d/formula.py index 2165e16..2434d89 100644 --- a/generat3d/formula.py +++ b/generat3d/formula.py @@ -123,7 +123,10 @@ class Graph: def _spline_op(name, stack): - n = int(stack.pop()) + n = stack.pop() + if isinstance(n, bool) or not isinstance(n, (int, float)) or n != int(n): + raise ValueError + n = int(n) if n < 1 or n > len(stack) - 1: raise IndexError knots = stack[-n:] @@ -135,6 +138,8 @@ def spline(kind, x, knots): """Evaluate a DSON spline at x. Knots are [x, y] or [x, y, t, c, b], sorted by x.""" xs = [k[0] for k in knots] ys = [k[1] for k in knots] + if any(a > b for a, b in zip(xs, xs[1:])): + raise ValueError("spline knots not sorted by x") if x <= xs[0]: return ys[0] if x >= xs[-1]: @@ -147,6 +152,7 @@ def spline(kind, x, knots): return ys[i] + s * (ys[i + 1] - ys[i]) # ponytail: Kochanek-Bartels with uniform segment parameterization; # if the corpus shows Daz scales tangents by knot spacing, add that here. + # end tangents use a one-sided difference (halved), check endpoints against the corpus too. m0 = _tcb_tangent(knots, ys, i, outgoing=True) m1 = _tcb_tangent(knots, ys, i + 1, outgoing=False) s2, s3 = s * s, s * s * s diff --git a/tests/test_formula.py b/tests/test_formula.py index 7440681..10065d2 100644 --- a/tests/test_formula.py +++ b/tests/test_formula.py @@ -168,3 +168,29 @@ class SplineTest(unittest.TestCase): g.add_prop("b") g.add_formula("b", spline_ops("spline_linear", [[0, 0], [1, 10]])) self.assertAlmostEqual(g.evaluate()["b"], 5.0) + + def test_fractional_count_is_fatal(self): + g = Graph() + g.add_prop("a", 0.5) + g.add_prop("b") + ops = [push("a"), val([0, 0]), val([1, 1]), val(2.5), {"op": "spline_linear"}] + g.add_formula("b", ops, source="f") + with self.assertRaisesRegex(Error, "malformed formula for b"): + g.evaluate() + + def test_unsorted_knots_are_fatal(self): + g = Graph() + g.add_prop("a", 0.5) + g.add_prop("b") + g.add_formula("b", spline_ops("spline_linear", [[1, 1], [0, 0], [2, 5]]), source="f") + with self.assertRaisesRegex(Error, "malformed formula for b"): + g.evaluate() + + def test_count_larger_than_stack_is_fatal(self): + g = Graph() + g.add_prop("a", 0.5) + g.add_prop("b") + ops = [push("a"), val(0), val(1), {"op": "spline_linear"}] + g.add_formula("b", ops, source="f") + with self.assertRaisesRegex(Error, "malformed formula for b"): + g.evaluate() -- cgit v1.2.3