aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generat3d/evaluate.py11
-rw-r--r--generat3d/scene.py11
-rw-r--r--tests/test_evaluate.py17
-rw-r--r--tests/test_scene.py12
4 files changed, 45 insertions, 6 deletions
diff --git a/generat3d/evaluate.py b/generat3d/evaluate.py
index c1a9f1b..173a78f 100644
--- a/generat3d/evaluate.py
+++ b/generat3d/evaluate.py
@@ -15,6 +15,11 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+import math
+
+import numpy as np
+
+from . import Error
from .scene import AXES, REST_CHANNELS, key
@@ -26,8 +31,12 @@ def evaluate(scene):
for k, (idx, deltas) in fig.morphs.items():
w = values.get(k, 0.0)
if w:
- verts[idx] += w * deltas # indices are unique within one morph
+ with np.errstate(all="ignore"):
+ verts[idx] += w * deltas # indices are unique within one morph
rest = {n.id: {ch: [values[key(n.id, n.id, f"{ch}/{a}")] for a in AXES]
for ch in REST_CHANNELS}
for n in fig.nodes}
+ if (not np.isfinite(verts).all()
+ or not all(math.isfinite(v) for r in rest.values() for c in r.values() for v in c)):
+ raise Error("evaluation produced non-finite values")
return verts, rest
diff --git a/generat3d/scene.py b/generat3d/scene.py
index cccfcff..722057f 100644
--- a/generat3d/scene.py
+++ b/generat3d/scene.py
@@ -63,8 +63,11 @@ class Scene:
def _finite(v):
- return (not isinstance(v, bool) and isinstance(v, (int, float))
- and math.isfinite(v))
+ try:
+ return (not isinstance(v, bool) and isinstance(v, (int, float))
+ and math.isfinite(v))
+ except OverflowError: # huge int
+ return False
def _channel(node, name):
@@ -86,8 +89,8 @@ def _lookup(library, doc, url, section):
def _number(url, v):
- if isinstance(v, bool) or not isinstance(v, (int, float)):
- raise Error(f"{url}: dial value is not a number")
+ if not _finite(v):
+ raise Error(f"{url}: dial value is not a finite number")
return v
diff --git a/tests/test_evaluate.py b/tests/test_evaluate.py
index 6668b0e..da5b840 100644
--- a/tests/test_evaluate.py
+++ b/tests/test_evaluate.py
@@ -17,9 +17,12 @@
import contextlib
import io
+import json
+import tempfile
import unittest
from pathlib import Path
+from generat3d import Error
from generat3d.evaluate import evaluate
from generat3d.library import Library
from generat3d.scene import load
@@ -37,3 +40,17 @@ class EvaluateTest(unittest.TestCase):
self.assertEqual(rest["hip"]["end_point"], [0, 8, 0])
self.assertEqual(rest["Fig"]["end_point"], [0, 10, 0])
self.assertEqual(scene.figure.verts.tolist()[0], [0, 0, 0]) # base untouched
+
+ def test_overflow_is_error(self):
+ with tempfile.TemporaryDirectory() as d:
+ root = Path(d) / "extra"
+ p = root / "data/Test/Fig/Morphs/Test/Big.dsf"
+ p.parent.mkdir(parents=True)
+ p.write_text(json.dumps({"modifier_library": [{
+ "id": "Big", "parent": "/data/Test/Fig/Fig.dsf#Fig",
+ "channel": {"id": "value", "value": 1e300},
+ "morph": {"vertex_count": 3, "deltas": {"values": [[0, 1e300, 0, 0]]}}}]}))
+ with contextlib.redirect_stderr(io.StringIO()):
+ scene = load(DATA / "scene.duf", Library([DATA / "lib", root]))
+ with self.assertRaisesRegex(Error, "non-finite"):
+ evaluate(scene)
diff --git a/tests/test_scene.py b/tests/test_scene.py
index 0d2e3c9..fe6c34d 100644
--- a/tests/test_scene.py
+++ b/tests/test_scene.py
@@ -170,7 +170,7 @@ class SceneTest(unittest.TestCase):
load_quiet(self.duf(scene))
def test_dial_not_a_number(self):
- with self.assertRaisesRegex(Error, "dial value is not a number"):
+ with self.assertRaisesRegex(Error, "dial value is not a finite number"):
load_quiet(self.duf({"nodes": self.base_nodes(), "modifiers": [
{"id": "Tall", "url": "/data/Test/Fig/Morphs/Test/Tall.dsf#Tall",
"parent": "#Fig", "channel": {"id": "value", "current_value": "a"}}]}))
@@ -271,3 +271,13 @@ class SceneTest(unittest.TestCase):
"output": "Hip:/data/Test/Fig2/Fig2.dsf#hip?center_point/y",
"operations": [{"op": "push", "val": 1}]}]})
self.assertEqual(len(scene.graph.formulas["hip:hip?center_point/y"]), 1)
+
+ def test_dial_not_finite(self):
+ for v in ("1e400", "10**400"):
+ text = json.dumps({"scene": {"nodes": self.base_nodes(), "animations": [
+ {"url": "Fig:#Tall?value", "keys": [[0, 0]]}]}}).replace(
+ '"keys": [[0, 0]]', '"keys": [[0, %s]]' % ("1e400" if v == "1e400" else "1" + "0" * 400))
+ p = self.dir / "s.duf"
+ p.write_text(text)
+ with self.assertRaisesRegex(Error, "dial value is not a finite number"):
+ load_quiet(p)