diff --git a/.gitignore b/.gitignore index 6e2260cdf..fc8e7f672 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *.pyc *.so -*~ -#* + .coverage coverage.xml gnofract4d.egg-info @@ -9,6 +8,7 @@ MANIFEST token .tox/* +.cache/* build/* dist/* experiments/* @@ -38,4 +38,5 @@ docs/manual/tags/* docs/manual/stdlib/* # edit -.vscode/ \ No newline at end of file +.vscode/ +*~ diff --git a/fract4d/fractal.py b/fract4d/fractal.py index 779555485..0b1f19f01 100644 --- a/fract4d/fractal.py +++ b/fract4d/fractal.py @@ -838,13 +838,13 @@ def calc(self, image, colormap, nthreads, site, asynchronous): dirty=self.clear_image, asynchronous=asynchronous) - def drawpoint(self): + def drawpoint(self, repeats=1000): self.init_pfunc() print("x:\t\t%.17f\ny:\t\t%.17f\nz:\t\t%.17f\nw:\t\t%.17f\n" % tuple(self.params[0:4])) startTime = now() result = fract4dc.pf_calc( - self.pfunc, self.params[0:4], self.maxiter, 0, 0, 0, 100 * 1000 * 1000) + self.pfunc, self.params[0:4], self.maxiter, 0, 0, 0, repeats) duration = now() - startTime print( "iterations:\t%s\nfate:\t\t%s\ndistance:\t%s\nsolid:\t\t%s" % diff --git a/fract4d/fractconfig.py b/fract4d/fractconfig.py index f94b9ebe7..ef2bbcfd1 100644 --- a/fract4d/fractconfig.py +++ b/fract4d/fractconfig.py @@ -230,15 +230,6 @@ def set_list(self, name, list): self.changed(name) - def update_list(self, name, new_entry, maxsize): - list = self.get_list(name) - if list.count(new_entry) == 0: - list.insert(0, new_entry) - list = list[:maxsize] - self.set_list(name, list) - - return list - def changed(self, section): pass diff --git a/fract4d/gradient.py b/fract4d/gradient.py index c6086013d..0018c2611 100644 --- a/fract4d/gradient.py +++ b/fract4d/gradient.py @@ -337,11 +337,11 @@ def load_ase(self, f): list = [] for i in range(nblocks): (block_type, block_length, name_length) = struct.unpack(">HLH", f.read(8)) - name_format = "%ds" % (name_length*2) + name_format = "%ds" % (name_length * 2) #print("%d %d %d" % (block_type, block_length, name_length)) - #print(name_format) - (name_bytes,) = struct.unpack(name_format, f.read(name_length*2)) - #print(name_bytes) + # print(name_format) + (name_bytes,) = struct.unpack(name_format, f.read(name_length * 2)) + # print(name_bytes) name = name_bytes.decode('utf_16_be') #print("name: %s" % name) if block_type == 1: @@ -351,8 +351,8 @@ def load_ase(self, f): #print("color model name: %s" % color_model_name) if color_model_name == "RGB ": - (r, g, b) = struct.unpack(">3f", f.read(3*4)) - (ri, gi, bi) = (int(r*255), int(g*255), int(b*255)) + (r, g, b) = struct.unpack(">3f", f.read(3 * 4)) + (ri, gi, bi) = (int(r * 255), int(g * 255), int(b * 255)) #print("color %f %f %f" % (r*255,g,b)) #print("color %x%x%x" % (ri,gi,bi)) else: @@ -368,7 +368,6 @@ def load_ase(self, f): self.load_list(list) - def load_ugr(self, f): "Load an ir tree parsed by the translator" prev_index = 0.0 @@ -413,7 +412,7 @@ def load_gimp_gradient(self, f): new_segments = [] name = None - right = rr = rg = rb = ra = 0 # should never be used + right = rr = rg = rb = ra = 0 # should never be used line = f.readline() if line.startswith("Name:"): @@ -505,7 +504,7 @@ def get_coolor_url(self): clist = [] for s in self.segments: color = s.left_color - (r, g, b, a) = [int(x*255.0) for x in color] + (r, g, b, a) = [int(x * 255.0) for x in color] colorstring = "%02x%02x%02x" % (r, g, b) clist.append(colorstring) return "https://coolors.co/" + ("-".join(clist)) @@ -515,15 +514,15 @@ def load_from_url(self, url): try: if not url.startswith("https://coolors.co/"): return False - url = url[19:] # remove domain + url = url[19:] # remove domain parts = url.split('-') - + colorlist = [] ncolors = len(parts) i = 0 for p in parts: (r, g, b) = (int(p[0:2], 16), int(p[2:4], 16), int(p[4:6], 16)) - entry = (i/ncolors, r, g, b, 255) + entry = (i / ncolors, r, g, b, 255) # print(entry) colorlist.append(entry) i += 1 @@ -580,19 +579,6 @@ def load_list(self, l, maxdiff=0): self.segments = new_segments - def load_fractint(self, l): - # l is a list of colors from a Fractint .par file - - # convert format to colorlist - i = 0 - colors = [] - for (r, g, b) in l: - colors.append((i / 255.0, r * 4, g * 4, b * 4, 255)) - i += 1 - # load it - - self.load_list(colors, -1.0) - def set_color(self, seg_id, is_left, r, g, b): if seg_id < 0 or seg_id >= len(self.segments): return False diff --git a/fract4d/tests/test_fractconfig.py b/fract4d/tests/test_fractconfig.py index 2d5e5528d..0cb595458 100644 --- a/fract4d/tests/test_fractconfig.py +++ b/fract4d/tests/test_fractconfig.py @@ -32,12 +32,32 @@ def testSetList(self): l2 = c.get_list("map_path") self.assertEqual(l, l2) + def testRemoveSectionItem(self): + c = fractconfig.T("") + l = c.get_list("map_path") + c.remove_section_item("map_path", 1) + l2 = c.get_list("map_path") + self.assertEqual(len(l) - 1, len(l2)) + self.assertEqual(l[0], l2[0]) + + for i in range(1, len(l2) - 1): + self.assertEqual(l[i + 1], l2[i]) + def testSetSize(self): c = self.userConfig c.set_size(871, 313) self.assertEqual(871, c.getint("display", "width")) self.assertEqual(313, c.getint("display", "height")) + def testSetSizeToSameNoChangeSignal(self): + c = self.userConfig + c.set_size(871, 313) + c.changed = self.assertNotCalled + c.set_size(871, 313) + + def assertNotCalled(self, *args): + self.assertFalse("Should not be called") + def testSave(self): testprefs = os.path.join(self.tmpdir.name, "testprefs") c = fractconfig.T(testprefs) diff --git a/fract4d/tests/test_fractmain.py b/fract4d/tests/test_fractmain.py new file mode 100644 index 000000000..ea866c567 --- /dev/null +++ b/fract4d/tests/test_fractmain.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# test fractmain + +import os.path + +from . import testbase + +from fract4d import fractmain +from fract4d.options import Arguments + + +class Test(testbase.TestSetup): + def testBasic(self): + c = self.userConfig + fm = fractmain.T(c) + options = Arguments().parse_args(["--save", "foo.png"]) + fm.run(options) + self.assertTrue(os.path.exists("foo.png")) + os.remove("foo.png") + + def testBuildOnly(self): + c = self.userConfig + fm = fractmain.T(c) + options = Arguments().parse_args(["--buildonly", "test.so"]) + + fm.run(options) + self.assertTrue(os.path.exists("test.so")) + os.remove("test.so") + if os.path.exists("test.so.c"): + os.remove("test.so.c") + + def testSinglePoint(self): + c = self.userConfig + fm = fractmain.T(c) + options = Arguments().parse_args(["--singlepoint"]) + + fm.run(options) diff --git a/fract4d/tests/test_gradient.py b/fract4d/tests/test_gradient.py index 073a44a78..c80df8eac 100644 --- a/fract4d/tests/test_gradient.py +++ b/fract4d/tests/test_gradient.py @@ -710,18 +710,30 @@ def testGetCoolorUrl(self): g.segments[0].left_color) url = g.get_coolor_url() - self.assertEqual("https://coolors.co/94ae89-a8bca1-c0da74-beedaa-d5ffd9", url) - + self.assertEqual( + "https://coolors.co/94ae89-a8bca1-c0da74-beedaa-d5ffd9", url) def testLoadUrl(self): g = gradient.Gradient() - g.load_from_url("https://coolors.co/94ae89-a8bca1-c0da74-beedaa-d5ffd9") + g.load_from_url( + "https://coolors.co/94ae89-a8bca1-c0da74-beedaa-d5ffd9") self.assertEqual(5, len(g.segments)) self.assertEqual( [0x94 / 255.0, 0xAE / 255.0, 0x89 / 255.0, 1.0], g.segments[0].left_color) + def testRandomizeComplementary(self): + g = gradient.Gradient() + g.randomize_complementary(10) + self.assertEqual(10 - 1, len(g.segments)) + self.assertWellFormedGradient(g) + + def testRandomizeSpheres(self): + g = gradient.Gradient() + g.randomize_spheres(10) + self.assertEqual(10, len(g.segments)) + self.assertWellFormedGradient(g) def testSetColor(self): g = gradient.Gradient() diff --git a/fract4d/tests/testbase.py b/fract4d/tests/testbase.py index 26020aa5c..95072aa4d 100644 --- a/fract4d/tests/testbase.py +++ b/fract4d/tests/testbase.py @@ -254,11 +254,10 @@ def setUpClass(cls): cls.userConfig.set("general", "cache_dir", os.path.join(cls.tmpdir.name, "gnofract4d-cache")) - cls.userConfig["formula_path"].clear() - cls.userConfig["map_path"].clear() + + cls.userConfig["formula_path"] = {"0": "formulas", + "1": "testdata/formulas"} cls.g_comp = fc.Compiler(cls.userConfig) - cls.g_comp.add_func_path("formulas") - cls.g_comp.add_func_path("testdata/formulas") @classmethod def tearDownClass(cls): @@ -269,13 +268,17 @@ def tearDownClass(cls): class TestSetup(TestBase): def setUp(self): self.tmpdir = tempfile.TemporaryDirectory(prefix="fract4d_") - self.userConfig = fractconfig.T("") + + import sys + if sys.platform[:6] == "darwin": + self.userConfig = fractconfig.DarwinConfig("") + else: + self.userConfig = fractconfig.T("") + self.userConfig.set("general", "cache_dir", os.path.join(self.tmpdir.name, "gnofract4d-cache")) - self.userConfig["formula_path"].clear() - self.userConfig["map_path"].clear() + self.userConfig["formula_path"] = {"0": "formulas"} self.g_comp = fc.Compiler(self.userConfig) - self.g_comp.add_func_path("formulas") def tearDown(self): del self.g_comp diff --git a/fract4dgui/preferences.py b/fract4dgui/preferences.py index f7b71e12f..ee2919196 100644 --- a/fract4dgui/preferences.py +++ b/fract4dgui/preferences.py @@ -54,9 +54,6 @@ def get_list(self, name): def set_list(self, name, list): self.config.set_list(name, list) - def update_list(self, name, new_entry, maxsize): - return self.config.update_list(name, new_entry, maxsize) - def remove_section_item(self, section, number): self.config.remove_section_item(section, number) diff --git a/setup.py b/setup.py index 52e572664..6183ad387 100755 --- a/setup.py +++ b/setup.py @@ -31,13 +31,16 @@ # Extensions need to link against appropriate libs # We use pkg-config to find the appropriate set of includes and libs + + def call_package_config(package, option, optional=False): '''invoke pkg-config, if it exists, to find the appropriate arguments for a library''' try: cp = subprocess.run(["pkg-config", package, option], universal_newlines=True, stdout=subprocess.PIPE) except FileNotFoundError: - print(f"Unable to check for '{package}', pkg-config not installed", file=sys.stderr) + print( + "Unable to check for '%s', pkg-config not installed" % package, file=sys.stderr) if optional: return [] else: @@ -45,11 +48,12 @@ def call_package_config(package, option, optional=False): if cp.returncode != 0: if optional: - print(f"Can't find '{package}'", file=sys.stderr) + print("Can't find '%s'" % package, file=sys.stderr) print("Some functionality will be disabled", file=sys.stderr) return [] else: - print(f"Development files not found for '{package}'", file=sys.stderr) + print( + "Development files not found for '%s'" % package, file=sys.stderr) sys.exit(1) return cp.stdout.split() @@ -99,7 +103,8 @@ def call_package_config(package, option, optional=False): ] define_macros.append(('THREADS', 1)) -extra_compile_args = ['-std=c++17', '-Wall', '-Wextra', '-Wpedantic'] +extra_compile_args = ['-std=c++17', '-Wall', + '-Wextra', '-Wpedantic', '-Wno-attributes'] # from https://pythonextensionpatterns.readthedocs.io/en/latest/compiler_flags.html?highlight=python3-dev if _DEBUG: @@ -125,16 +130,19 @@ def call_package_config(package, option, optional=False): define_macros=define_macros, ) + def get_files(dir, ext): return [os.path.join(dir, x) for x in os.listdir(dir) if x.endswith(ext)] + def get_icons(): icons = [] for size in 16, 32, 48, 64, 128, 256: icons.append((f'share/icons/hicolor/{size}x{size}/apps', - [f'pixmaps/logo/{size}x{size}/gnofract4d.png'])) + [f'pixmaps/logo/{size}x{size}/gnofract4d.png'])) return icons + so_extension = sysconfig.get_config_var("EXT_SUFFIX") setup(