"""Line drawings (SVG and the fan-art designs) joined into one sand-table path. Run from the software folder: python3 -m unittest discover -s tests -v""" import math import os import sys import tempfile import unittest os.environ['SANDTABLE_QUIET'] = '1' sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'tools')) from sandtable import config, thr from sandtable.lineart import drawing_to_polar, join_strokes, path_polylines, svg_strokes SVG = """ ignored """ def unit_xy(pts): return [(r * math.cos(t), r * math.sin(t)) for t, r in pts] class TestSvg(unittest.TestCase): def test_shapes_and_transforms(self): s = svg_strokes(SVG) self.assertEqual(len(s), 3) # defs and text are skipped circle = s[1] self.assertAlmostEqual(max(p[0] for p in circle), 70, delta=0.1) # r 10 scaled 2x, moved to 50 self.assertAlmostEqual(min(p[0] for p in circle), 30, delta=0.1) path = s[2] self.assertAlmostEqual(math.dist(path[0], path[-1]), 0, places=6) # z closes it self.assertAlmostEqual(min(p[1] for p in path), 70, delta=0.6) # the arc's top (centre 70,80, r 10) def test_arc_and_relative(self): (semi,) = path_polylines('M0 0 a10 10 0 0 1 20 0', tol=0.1) self.assertAlmostEqual(min(p[1] for p in semi), -10, delta=0.05) # sweep 1 bulges up the page (y down) lines = path_polylines('m0 0 l10 0 10 0 M0 5 H20 V9') self.assertEqual(lines[0][-1], (20.0, 0.0)) # implicit repeated l self.assertEqual(lines[1][-1], (20.0, 9.0)) class TestJoin(unittest.TestCase): def check_path(self, xy, strokes): for a, b in zip(xy, xy[1:]): self.assertLess(math.dist(a, b), 0.008) # one continuous line for end in (xy[0], xy[-1]): r = math.hypot(*end) self.assertTrue(r < 1e-9 or abs(r - 1) < 1e-9, end) # starts and ends at the centre or the edge for st in strokes: # every stroke is drawn for p in st[::7]: self.assertLess(min(math.dist(p, q) for q in xy), 0.01) def test_triangle_from_three_lines_needs_no_jumps(self): a, b, c = (0.0, 0.85), (-0.74, -0.42), (0.74, -0.42) strokes = [[a, b], [c, b], [c, a]] stats = {} xy = join_strokes(strokes, stats=stats) self.check_path(xy, strokes) self.assertLess(stats['jump'], 0.35) # only the short lines in from and out to the edge def test_separate_shapes(self): sq = lambda x, y, d: [(x, y), (x + d, y), (x + d, y + d), (x, y + d), (x, y)] strokes = [sq(-0.6, -0.6, 0.3), sq(0.3, 0.3, 0.3), sq(0.3, -0.6, 0.3), [(-0.5, 0.5), (-0.2, 0.2)]] xy = join_strokes(strokes) self.check_path(xy, strokes) def test_fan_art_designs(self): try: import make_fan_art # not in the public repository (trademarks) except ImportError: self.skipTest('tools/make_fan_art.py is not here') for name, (fn, opts) in make_fan_art.DESIGNS.items(): pts = drawing_to_polar(fn(), **opts) self.assertGreater(len(pts), 100, name) for end in (pts[0][1], pts[-1][1]): self.assertTrue(end < 0.02 or end > 0.98, f'{name} ends at rho {end}') xy = unit_xy(pts) for a, b in zip(xy, xy[1:]): self.assertLess(math.dist(a, b), 0.06, name) class TestUpload(unittest.TestCase): def test_svg_upload_becomes_a_design(self): from sandtable.web import Library with tempfile.TemporaryDirectory() as d: lib = Library(config.load('/nonexistent.toml'), root=os.path.join(d, 'p'), cache=os.path.join(d, 'c'), removed=os.path.join(d, 'r')) ident = lib.add('My Logo.svg', SVG) self.assertEqual(ident, 'uploads/My-Logo.thr') pts = thr.load(lib.path(ident)) self.assertGreater(len(pts), 100) with self.assertRaises(ValueError): lib.add('empty.svg', 'hi') if __name__ == '__main__': unittest.main()