diff --git a/.github/workflows/test-gnofract4d.yml b/.github/workflows/test-gnofract4d.yml index 7e5ebc1b5..6e3d96225 100644 --- a/.github/workflows/test-gnofract4d.yml +++ b/.github/workflows/test-gnofract4d.yml @@ -20,7 +20,7 @@ jobs: python: 3.6 toxenv: py - os: ubuntu-latest - python: 3.7 + python: 3.8 toxenv: pylint env: diff --git a/README.md b/README.md index b0b0aa326..264a5cbbf 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ You can then run Gnofract 4D in the local directory: ./gnofract4d -Alternatively to build and install Gnofract 4D (version 4.2 as an example) type: +Alternatively to build and install Gnofract 4D type: - pip3 install gnofract4d-4.2.tar.gz + pip3 install gnofract4d-4.3.tar.gz You can then run it by clicking on the desktop icon or typing: diff --git a/fract4d/options.py b/fract4d/options.py index a4ab0b70c..0c66d25d5 100644 --- a/fract4d/options.py +++ b/fract4d/options.py @@ -5,7 +5,7 @@ from . import fractal # version of Gnofract 4D -VERSION = '4.2' +VERSION = '4.3' POSITION_ARGUMENTS = ("xcenter", "ycenter", "zcenter", "wcenter", "xyangle", "xzangle", "xwangle", "yzangle", "ywangle", "zwangle", diff --git a/fractutils/__init__.py b/fractutils/__init__.py deleted file mode 100644 index 4e741b925..000000000 --- a/fractutils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env python - -# package file for Gnofract4d utils package - diff --git a/fractutils/checkalloc.py b/fractutils/checkalloc.py deleted file mode 100755 index a5d1d4204..000000000 --- a/fractutils/checkalloc.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -# utility script. When compiled with DEBUG_CREATION and/or DEBUG_ALLOCATION, -# we output debug spew whenever C objects used from Python are created or deleted - -# This script checks whether the creations and deletion match - -# usage: ./test_fract4d.py 2>&1 | ./checkalloc.py - -import sys -import re - -alloc_re = re.compile(r'(?P.*?... )?\.?(?P0x[0-9a-f]+)\s*:\s*(?P.*?)\s*:\s*(?P[A-Z]+)') - -if len(sys.argv) > 1: - lines = open(sys.argv[1]).readlines() -else: - lines = sys.stdin.readlines() - -ops = {} - -for line in lines: - m = alloc_re.match(line) - if m: - if m.group("test"): - test = m.group("test") - pointer = m.group("ptr") - type = m.group("type") - op = m.group("op") - if op == "REF": - # it's OK to 'create' the same dlhandle several times - # since those are refcounts - ops[pointer] = ops.get(pointer,0) + 1 - elif op == "DEREF": - x = ops.get(pointer) - if x is None: - raise Exception("deref on object with no refs: %s" % pointer) - if x == 1: - del ops[pointer] - else: - ops[pointer] = x - 1 - elif op == "CTOR": - if ops.get(pointer): - raise Exception("double alloc on %s" % pointer) - ops[pointer] = type - elif op == "DTOR": - alloctype = ops.get(pointer) - if not alloctype: - raise Exception("dtor on unallocated pointer %s" % pointer) - if type != alloctype: - raise Exception("different dealloc type for %s" % pointer) - del ops[pointer] - else: - print("unrecognized op %s" % op) - else: - print("skipped: %s" % line, end=' ') - -print(list(ops.items())) -for (k,v) in list(ops.items()): - raise Exception("%s(%s) never freed" % (k,v)) - -print("ok!") diff --git a/fractutils/fetch.py b/fractutils/fetch.py deleted file mode 100644 index 74e51663d..000000000 --- a/fractutils/fetch.py +++ /dev/null @@ -1,12 +0,0 @@ -# utilities for fetching web pages in a non-blocking way - -import os - -GET_CMD=os.path.join(os.path.dirname(__file__),"get.py") - -class Request(object): - def __init__(self, cmd, args, input): - self.cmd = cmd - self.args = args - self.input = input - diff --git a/fractutils/formdb.py b/fractutils/formdb.py deleted file mode 100644 index 534015027..000000000 --- a/fractutils/formdb.py +++ /dev/null @@ -1,25 +0,0 @@ - -import re - -from . import slave, fetch - -target_base = "http://formulas.ultrafractal.com/cgi-bin/" - -href_re = re.compile(r' maxcount: - maxchild = child - maxcount = child.count - return (maxchild, totalchildren) - - def get_collapse_error(self,node): - "How much the image's error will increase if this node is collapsed" - # only works on internal nodes which only have leaves as children - if not node or node.isleaf(): - return 0 - (maxchild, totalchildren) = self.get_collapse_info(node) - - error = 0 - for child in node.branches: - if child: - error += child.difference_from(maxchild) - return error - - def find_collapse_candidates(self,node,candidates): - if not node or node.isleaf(): - return candidates - - has_children = False - for b in node.branches: - if b and not b.isleaf(): - self.find_collapse_candidates(b,candidates) - has_children = True - - if not has_children: - cost = self.get_collapse_error(node) - candidates.append((cost,node)) - return candidates - - def collapse(self,node): - '''Collapse all children into this node, getting the most - popular child color''' - if not node or node.isleaf(): - raise ArgumentError("Can't collapse") - - (maxchild,totalchildren) = self.get_collapse_info(node) - node._isleaf = True - node.branches = [None] * 8 - node.count = totalchildren - node.r = maxchild.r - node.g = maxchild.g - node.b = maxchild.b - - def getBranch(self,r,g,b,nr,ng,nb): - branch = 0 - if r > nr: - branch += 4 - if g > ng: - branch += 2 - if b > nb: - branch += 1 - return branch - - def getInteriorRGB(self,r,g,b,nr,ng,nb,size): - if r > nr: - nr += size - else: - nr -= size - if g > ng: - ng += size - else: - ng -= size - if b > nb: - nb += size - else: - nb -= size - return (nr,ng,nb) - - def insertNode(self,parent,r,g,b,count,nr,ng,nb,size): - if parent == None: - parent = self.addLeafNode(r,g,b,count) - elif parent.matches(r,g,b) and parent.isleaf(): - parent.count += 1 - elif parent.isleaf(): - # replace it with a new interior node, reinsert this leaf and the new one - currentleaf = parent - parent = self.addInternalNode(nr,ng,nb) - currentbranch = self.getBranch( - currentleaf.r,currentleaf.g,currentleaf.b,nr,ng,nb) - newbranch = self.getBranch( - r,g,b,nr,ng,nb) - if currentbranch == newbranch: - # need to subdivide further - (nr,ng,nb) = self.getInteriorRGB(r,g,b,nr,ng,nb,size/2) - parent.branches[newbranch] = self.addInternalNode(nr,ng,nb) - parent.branches[newbranch] = self.insertNode( - parent.branches[newbranch],r,g,b,1,nr,ng,nb,size/2) - parent.branches[newbranch] = self.insertNode( - parent.branches[newbranch], - currentleaf.r, currentleaf.g, currentleaf.b, - currentleaf.count, - nr,ng,nb,size/2) - else: - parent.branches[currentbranch] = currentleaf - parent.branches[newbranch] = self.addLeafNode(r,g,b,1) - else: - # parent is an interior node, recurse to appropriate branch - newbranch = self.getBranch(r,g,b,nr,ng,nb) - (nr,ng,nb) = self.getInteriorRGB(r,g,b,nr,ng,nb,size/2) - parent.branches[newbranch] = self.insertNode( - parent.branches[newbranch],r,g,b,1,nr,ng,nb,size/2) - return parent - - def insertPixel(self,r,g,b): - self.root = self.insertNode(self.root,r,g,b,1,127,127,127,128) - - def numColors(self): - return self._numColors(self.root) - - def _numColors(self,node): - if not node: - return 0 - if node.isleaf(): - return 1 - - colors = 0 - return sum(map(self._numColors,node.branches)) - - def _colors(self,node,list): - if not node: - return - if node.isleaf(): - list.append((node.r,node.g,node.b)) - for b in node.branches: - self._colors(b,list) - return list - - def colors(self): - return self._colors(self.root,[]) - - def reduceColors(self,n): - while self.numColors() > n: - candidates = self.find_collapse_candidates(self.root,[]) - node_to_collapse = candidates[0][1] - self.collapse(node_to_collapse) - - -def main(args): - mm = T() - mm.load(open(args[0])) - mm.build(4) - - mm.reduceColors(int(args[1])) - - grad = gradient.Gradient() - - colors = [] - i = 0 - for (r,g,b) in mm.colors(): - colors.append((i/10.0,r,g,b,255)) - i += 1 - - grad.load_list(colors) - grad.save(sys.stdout) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/fractutils/makemapgui.py b/fractutils/makemapgui.py deleted file mode 100644 index f5d19c8cd..000000000 --- a/fractutils/makemapgui.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python - -# create a color map based on an image. -# vaguely octree-inspired - -import sys - -sys.path.append("..") -from fract4d import gradient - -from PIL import ImageFile - -import gtk - -class Node: - def __init__(self,r,g,b,count): - self.branches = [None] * 8 - self.r = r - self.g = g - self.b = b - self.count = count - self._isleaf = True - - def isleaf(self): - return self._isleaf - - def matches(self,r,g,b): - return self.r == r and self.g == g and self.b == b - - def difference_from(self,node): - dr = self.r - node.r - dg = self.g - node.g - db = self.b - node.b - return (dr*dr + dg*dg + db*db) * self.count - -class T: - R = 0 - G = 1 - B = 2 - def __init__(self): - 'Load an image from open stream "file"' - self.root = None - - def addLeafNode(self,r,g,b,count): - return Node(r,g,b,count) - - def addInternalNode(self,r,g,b): - n = Node(r,g,b,0) - n._isleaf = False - return n - - def load(self,file): - p = ImageFile.Parser() - while 1: - s = file.read(1024) - if not s: - break - p.feed(s) - - self.im = p.close() - - def getdata(self): - return self.im.getdata() - - def build(self,divby=1): - i = 0 - for (r,g,b) in self.getdata(): - if i % divby == 0: - self.insertPixel(r,g,b) - i += 1 - - def dump(self,node,indent=""): - if not node: - return "" - - if node.isleaf(): - leafness = "L" - else: - leafness = "I" - val = [ indent + "[(%s,%d,%d,%d,%d)" % \ - (leafness, node.r, node.g, node.b, node.count)] - val += [self.dump(b,indent+" ") for b in node.branches] - val += [ indent + "]"] - return "\n".join(val) - - def get_collapse_info(self,node): - maxchild = None - maxcount = 0 - totalchildren = 0 - for child in node.branches: - if child: - totalchildren += child.count - if child.count > maxcount: - maxchild = child - maxcount = child.count - return (maxchild, totalchildren) - - def get_collapse_error(self,node): - "How much the image's error will increase if this node is collapsed" - # only works on internal nodes which only have leaves as children - if not node or node.isleaf(): - return 0 - (maxchild, totalchildren) = self.get_collapse_info(node) - - error = 0 - for child in node.branches: - if child: - error += child.difference_from(maxchild) - return error - - def find_collapse_candidates(self,node,candidates): - if not node or node.isleaf(): - return candidates - - has_children = False - for b in node.branches: - if b and not b.isleaf(): - self.find_collapse_candidates(b,candidates) - has_children = True - - if not has_children: - cost = self.get_collapse_error(node) - candidates.append((cost,node)) - return candidates - - def collapse(self,node): - '''Collapse all children into this node, getting the most - popular child color''' - if not node or node.isleaf(): - raise ArgumentError("Can't collapse") - - (maxchild,totalchildren) = self.get_collapse_info(node) - node._isleaf = True - node.branches = [None] * 8 - node.count = totalchildren - node.r = maxchild.r - node.g = maxchild.g - node.b = maxchild.b - - def getBranch(self,r,g,b,nr,ng,nb): - branch = 0 - if r > nr: - branch += 4 - if g > ng: - branch += 2 - if b > nb: - branch += 1 - return branch - - def getInteriorRGB(self,r,g,b,nr,ng,nb,size): - if r > nr: - nr += size - else: - nr -= size - if g > ng: - ng += size - else: - ng -= size - if b > nb: - nb += size - else: - nb -= size - return (nr,ng,nb) - - def insertNode(self,parent,r,g,b,count,nr,ng,nb,size): - if parent == None: - parent = self.addLeafNode(r,g,b,count) - elif parent.matches(r,g,b) and parent.isleaf(): - parent.count += 1 - elif parent.isleaf(): - # replace it with a new interior node, reinsert this leaf and the new one - currentleaf = parent - parent = self.addInternalNode(nr,ng,nb) - currentbranch = self.getBranch( - currentleaf.r,currentleaf.g,currentleaf.b,nr,ng,nb) - newbranch = self.getBranch( - r,g,b,nr,ng,nb) - if currentbranch == newbranch: - # need to subdivide further - (nr,ng,nb) = self.getInteriorRGB(r,g,b,nr,ng,nb,size/2) - parent.branches[newbranch] = self.addInternalNode(nr,ng,nb) - parent.branches[newbranch] = self.insertNode( - parent.branches[newbranch],r,g,b,1,nr,ng,nb,size/2) - parent.branches[newbranch] = self.insertNode( - parent.branches[newbranch], - currentleaf.r, currentleaf.g, currentleaf.b, - currentleaf.count, - nr,ng,nb,size/2) - else: - parent.branches[currentbranch] = currentleaf - parent.branches[newbranch] = self.addLeafNode(r,g,b,1) - else: - # parent is an interior node, recurse to appropriate branch - newbranch = self.getBranch(r,g,b,nr,ng,nb) - (nr,ng,nb) = self.getInteriorRGB(r,g,b,nr,ng,nb,size/2) - parent.branches[newbranch] = self.insertNode( - parent.branches[newbranch],r,g,b,1,nr,ng,nb,size/2) - return parent - - def insertPixel(self,r,g,b): - self.root = self.insertNode(self.root,r,g,b,1,127,127,127,128) - - def numColors(self): - return self._numColors(self.root) - - def _numColors(self,node): - if not node: - return 0 - if node.isleaf(): - return 1 - - colors = 0 - return sum(map(self._numColors,node.branches)) - - def _colors(self,node,list): - if not node: - return - if node.isleaf(): - list.append((node.r,node.g,node.b)) - for b in node.branches: - self._colors(b,list) - return list - - def colors(self): - return self._colors(self.root,[]) - - def reduceColors(self,n): - while self.numColors() > n: - candidates = self.find_collapse_candidates(self.root,[]) - self.collapse(candidates[0][1]) - - -class MapMaker(gtk.Window): - def __init__(self,type=gtk.WINDOW_TOPLEVEL): - gtk.Window.__init__(self,type) - self.image = gtk.Image() - self.add(self.image) - self.resize(640,480) - self.connect('delete-event', self.quit) - - def load(self,name): - self.image.set_from_file(name) - - def quit(self,*args): - gtk.main_quit() - -def main(args): - w = MapMaker() - w.load(args[0]) - w.show_all() - - - gtk.main() - -def old_main(args): - mm = T() - mm.load(open(args[0])) - mm.build(1) - - mm.reduceColors(int(args[1])) - - grad = gradient.Gradient() - - colors = [] - i = 0 - for (r,g,b) in mm.colors(): - colors.append((i/10.0,r,g,b,255)) - i += 1 - - grad.load_list(colors) - grad.save(sys.stdout) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/fractutils/mapmatcher.py b/fractutils/mapmatcher.py deleted file mode 100755 index 3db73c8ca..000000000 --- a/fractutils/mapmatcher.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 - -# Usage: python mapmatcher.py *.map - -import sys -import re - -rgb_re = re.compile(r'\s*(\d+)\s+(\d+)\s+(\d+)') - -def build_list(mapfile): - colorlist = [] - for line in mapfile: - m = rgb_re.match(line) - if m is not None: - (r,g,b) = (min(255, int(m.group(1))), - min(255, int(m.group(2))), - min(255, int(m.group(3)))) - colorlist.append((r,g,b)) - return colorlist - -def rotate_list_by(l,n): - return l[n:] + l[:n] - -sets = {} - -for f in sys.argv[1:]: - l = build_list(open(f)) - min_list = l - for i in range(len(l)): - rotated_list = rotate_list_by(l,i) - if rotated_list < min_list: - min_list = rotated_list - - # min_list is now the canonical rotation - - # convert the list to a string since lists can't be used as hash keys - key = "%s" % min_list - - # add the filename to a hash indexed by the rotated list - sets.setdefault(key, []).append(f) - -for (k,v) in list(sets.items()): - if len(v) > 1: - # some dups - print("These files contain the same map:") - print("\n".join(v)) diff --git a/fractutils/parfile.py b/fractutils/parfile.py deleted file mode 100644 index b5ea00048..000000000 --- a/fractutils/parfile.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python3 - -# rudimentary read-only support for Fractint PAR files - -# issues discovered while looking at fotd3.par -# y needs to be negative (?) -# use gf4d.cfrm#default - continuous potential doesn't work? -# rotation == -xyangle in degrees, needs convert to radians - -import math - -from fract4d import fractconfig -from fract4d_compiler import preprocessor - -def parse(file,f): - # reset the fractal to have defaults closer to Fractint - f.set_outer("gf4d.cfrm","default") - f.yflip = True - - params = get_params(file) - pairs = get_param_pairs(params) - - formulaname = pairs.get("formulaname","Mandelbrot") - formulafile = pairs.get("formulafile","gf4d.frm") - - f.set_formula(formulafile, formulaname) - for (k,v) in list(pairs.items()): - if k == "maxiter": parse_maxiter(v,f) - elif k == "center-mag" : parse_center_mag(v,f) - elif k == "colors" : parse_colors(v,f) - elif k == "params" : parse_params(v,f) - elif k == "logmap" : parse_logmap(v,f) - -def parse_params(val,f): - paramlist = val.split("/") - l = len(paramlist)//2 - for i in range(l): - (re,im) = (paramlist[i*2],paramlist[i*2+1]) - name = "@p%d" % (i+1) - val = "(%s,%s)" % (re,im) - f.forms[0].set_named_param(name,val) - -def get_params(file): - return preprocessor.T(file.read()).out().split() - -def parse_logmap(val,f): - f.set_outer("fractint.ucl","outside") - f.forms[1].set_named_param("@logmap",val) - -def get_param_pairs(params): - pairs = {} - for p in params: - vals = p.split("=") - if len(vals) == 2: - pairs[vals[0]] = vals[1] - return pairs - -def parse_maxiter(val,f): - max = int(val) - f.maxiter = max - -def parse_colors(val,f): - colors = colorRange(val) - f.get_gradient().load_fractint(colors) - -def parse_center_mag(val,f): - "x/y/mag(/xmag/rot/skew)" - vals = val.split("/") - x = float(vals[0]) - y = -float(vals[1]) - mag = float(vals[2]) - f.params[f.XCENTER] = x - f.params[f.YCENTER] = y - h = 2.0/mag - f.params[f.MAGNITUDE] = h * 1.33 - - if len(vals) > 3: - xmag = float(vals[3]) - if len(vals) > 4: - rot = float(vals[4]) * -1 * math.pi / 180.0 - f.params[f.XYANGLE] = rot - if len(vals) > 5: - skew = float(vals[5]) - -def setup_log_table(log_flag, maxltsize, colors, save_release): - # try to match convoluted Fractint log_table logic - (lf,mlf) = get_log_table_limits(log_flag, maxltsize, colors, save_release) - table = [ - calc_log_table_entry(x,log_flag,lf,mlf, save_release) - for x in range(maxltsize) - ] - return table - -def calc_log_table_entry(n, log_flag, lf,mlf, save_release): - if log_flag > 0: - if n <= lf: - return 1 - - try: - if (n-lf) / math.log(n - lf) <= mlf: - if save_release < 2002: - if lf: - flag = 1 - else: - flag - 0 - return n - lf + flag - else: - return n - lf - except ZeroDivisionError: - pass - - return int(mlf * math.log(n - lf)) + 1 - - return 0 - -def get_log_table_limits(log_flag, maxltsize, colors, save_release): - if save_release > 1920: - if log_flag > 0: - lf = log_flag - if log_flag < 1: - lf = 0 - if lf >= maxltsize: - lf = maxltsize - 1 - if lf != 0: - delta = 2 - else: - delta = 1 - mlf = (colors - delta) / math.log(maxltsize - lf) - return (lf,mlf) - -def decode_val(c): - if c >= '0' and c <= '9': - return 4 * (ord(c) - ord('0')) - elif c >= 'A' and c <= 'Z': - return 4 * (ord(c) - ord('A') + 10) - elif c == '_': - return 4 * 36 - elif c == '`': - return 4 * 37 - elif c >= 'a' and c <= 'z': - return 4 * (ord(c) - ord('a') + 38) - else: - raise RuntimeError("Invalid character %s in colors" % c) - -def colorRange(s): - '''From help4.src: - - The colors= parameter in a PAR entry is a set of triplets. Each - triplet represents a color in the saved palette. The triplet is - made from the red green and blue components of the color in the - palette entry. The current limitations of fractint\'s palette - handling capabilities restrict the palette to 256 colors. Each - triplet rgb component is a 6 bit value from 0 to 63. These values - are encoded using the following scheme: - - rgb value => encoded value - 0 - 9 => 0 - 9 - 10 - 35 => A - Z - 36 - 37 => _ - ` - 38 - 63 => a - z - - In addition, Pieter Branderhorst has incorporated a way to - compress the encoding when the image has smooth-shaded ranges. - These ranges are written as with the nn representing the - number of entries between the preceeding triplet and the following - triplet.''' - - colors = [] - i = 0 - runlength = 0 - while i < len(s): - c = s[i] - if c == '<': - j = s.find(">", i) - if j == -1: - raise RuntimeError("No > after < in colors") - runlength = int(s[i+1:j]) - if runlength == 0: - raise RuntimeError("Zero runlength") - i = j+1 - else: - if len(s) < i+3: - raise RuntimeError("invalid color string") - rgb = list(map(decode_val, list(s[i:i+3]))) - if runlength > 0: - if len(colors) == 0: - raise RuntimeError("run with no preceding color") - pairs = list(zip(colors[-1],rgb)) - for k in range(0,runlength): - ratio = (k+1.0) / runlength - nratio = 1.0 - ratio - col = [int(x_y[0] * nratio + x_y[1] * ratio) for x_y in pairs] - colors.append(col) - - colors.append(rgb) - i += 3 - runlength = 0 - - return colors - - -if __name__ == "__main__": - import sys - from fract4d import fractal - from fract4d_compiler import fc - - g_comp = fc.Compiler(fractconfig.userConfig()) - g_comp.add_func_path("../formulas") - g_comp.add_func_path("../testdata/formulas") - g_comp.load_formula_file("gf4d.frm") - g_comp.load_formula_file("test.frm") - g_comp.load_formula_file("gf4d.cfrm") - - f = fractal.T(g_comp) - file = open(sys.argv[1]) - - parse(file,f) - - f.save(open("parfile.fct","w")) diff --git a/fractutils/ppfrm.py b/fractutils/ppfrm.py deleted file mode 100755 index 94cef0318..000000000 --- a/fractutils/ppfrm.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 - -# pretty-print a formula in docbook or HTML format - -# std modules -import sys -import re - -# kid -import elementtree.ElementTree -import kid -kid.enable_import() - -# kid templates -import frm_docbook - -# PLY -from fract4d import lex - -# my code -from fract4d import fractlexer - -def output_frm(toks,outbase, nfrms): - if toks != []: - outfile = open("%s%03d.xml" % (outbase, nfrms),"w") - k = frm_docbook.Template(tokens=toks) - print(k.serialize(), file=outfile) - -# map from token types -> docbook element types -element_types = { - "FORM_ID" : '', - "SECT_SET" : '', - "SECT_PARMS" : '', - "SECT_STM" : '', - "NUMBER" : '', - "COMMENT" : '' - } - -# array of words to highlight for each formula in the tutorial -highlights = [ - {}, - {}, - {"#zwpixel": ''}, - {"@myfunc": '', - "@factor": ''} - ] - -myfrm = re.compile('MyFormula\d+') - -def processToken(tok, specialdict): - element_type = element_types.get(tok.type) - special_type = specialdict.get(tok.value) - if special_type: - val = elementtree.ElementTree.XML(special_type) - val.text = tok.value - elif element_type: - val = elementtree.ElementTree.XML(element_type) - val.text = tok.value - else: - val = tok.value - return val - -def main(infile,outbase): - fractlexer.keep_all = True - fractlexer.t_ignore = "" - flex = lex.lex(fractlexer) - - flex.input(open(infile).read()) - - # Tokenize - toks = [] - nfrms = 0 - while 1: - tok = flex.token() - if not tok: break # No more input - - #element.text = tok.value - if tok.type == "FORM_ID": - output_frm(toks,outbase,nfrms) - toks = [] - nfrms += 1 - # special case for processing tutorial - tok.value = myfrm.sub('MyFormula',tok.value) - - toks.append(processToken(tok, highlights[nfrms])) - - # print last formula - output_frm(toks,outbase,nfrms) - - -if __name__ == '__main__': - if len(sys.argv) < 2: - print("Usage: ppfrm.py foo.frm outbase") - sys.exit(1) - - if(len(sys.argv) > 2): - main(sys.argv[1], sys.argv[2]) - else: - main(sys.argv[1]) diff --git a/fractutils/profutil.py b/fractutils/profutil.py deleted file mode 100755 index c50ff9640..000000000 --- a/fractutils/profutil.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 - -# A utility program used to create a standalone single-threaded -# statically-linked C program for profiling purposes - -import sys -import subprocess - -from fract4d import fc, fractal - -class PC(fc.Compiler): - def __init__(self): - fc.Compiler.__init__(self) - - self.cfiles = [ - "profharness.cpp", - "c/cmap.cpp", - "c/image.cpp", - "c/fractFunc.cpp", - "c/fract_stdlib.cpp", - "c/MTFractWorker.cpp", - "c/pointFunc.cpp", - "c/STFractWorker.cpp", - "c/imageWriter.cpp" - ] - -def main(args): - pc = PC() - pc.add_func_path("../formulas") - pc.load_formula_file("gf4d.frm") - pc.load_formula_file("gf4d.cfrm") - pc.compiler_name = "g++" - pc.leave_dirty = True - f = fractal.T(pc) - f.loadFctFile(open(args[0])) - outfile = f.compile() - cfile = outfile[:-2] + "c" - - # compile the stub and the c file to create a program to profile - files = " ".join(pc.cfiles + [cfile]) - - cmd = "%s %s %s -o %s %s" % \ - (pc.compiler_name, files, "-g -pg -O3 -Ic -lpthread", "proftest", "") - - print(cmd) - (status,output) = subprocess.getstatusoutput(cmd) - if status != 0: - raise Exception( - "Error reported by C compiler:%s" % output) - - print(output) - # compiled - hurrah! run it - (status,output) = subprocess.getstatusoutput("./proftest") - if status != 0: - raise Exception( - "Error reported by program:%s" % output) - - print(output) - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/fractutils/slave.py b/fractutils/slave.py deleted file mode 100644 index 4a50b8545..000000000 --- a/fractutils/slave.py +++ /dev/null @@ -1,233 +0,0 @@ -# slave.py - run a subordinate process asynchronously - -import sys -import os - -if "win" == sys.platform[:3]: - import win32api -else: - import fcntl - -import signal -import select -import errno -import time - -import subprocess - -def makeNonBlocking(fd): - fl = fcntl.fcntl(fd, fcntl.F_GETFL) - try: - fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY) - except AttributeError: - import FCNTL - fcntl.fcntl(fd, FCNTL.F_SETFL, fl | FCNTL.FNDELAY) - -class Slave(object): - def __init__(self, cmd, *args): - self.cmd = cmd - self.args = list(args) - self.process = None - self.input = "" - self.in_pos = 0 - self.stdin = None - self.stdout = None - self.output = "" - self.err_output = "" - self.dead = False - - def run(self, input): - self.input = input - args = [self.cmd, str(len(input))] + self.args - #print args - self.process = subprocess.Popen( - args, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - close_fds=True) - - makeNonBlocking(self.process.stdin.fileno()) - makeNonBlocking(self.process.stdout.fileno()) - makeNonBlocking(self.process.stderr.fileno()) - self.stdin = self.process.stdin - self.stdout = self.process.stdout - self.stderr = self.process.stderr - - def write(self): - if self.dead: - self.on_finish_writing() - return False - - bytes_to_write = min(len(self.input) - self.in_pos,1000) - if bytes_to_write < 1: - self.stdin.close() - self.on_finish_writing() - return False - - try: - self.stdin.write( - self.input[self.in_pos:self.in_pos+bytes_to_write]) - #print "wrote %d" % bytes_to_write - except IOError as err: - if err.errno == errno.EAGAIN: - #print "again!" - return True - raise - - self.in_pos += bytes_to_write - return True - - def read(self): - if self.dead: - self.on_complete() - return False - try: - data = self.stdout.read(-1) - #print "read", len(data) - if data == "": - # checking all these ways to see if child has died - # since they don't seem to be reliable - if self.process.poll() == None or self.process.returncode != None: - return False - except IOError as err: - if err.errno == errno.EAGAIN: - #print "again!" - return True - raise - self.output += data - return True - - def read_stderr(self): - if self.dead: - self.on_complete() - return False - try: - data = self.stderr.read(-1) - #print "read", len(data) - if data == "": - # checking all these ways to see if child has died - # since they don't seem to be reliable - if self.process.poll() == None or self.process.returncode != None: - self.on_complete() - return False - except IOError as err: - if err.errno == errno.EAGAIN: - #print "again!" - return True - raise - self.err_output += data - return True - - def on_complete(self): - pass - - def on_finish_writing(self): - pass - - def terminate(self): - try: - self.dead = True - os.kill(self.process.pid,signal.SIGKILL) - except OSError as err: - if err.errno == errno.ESRCH: - # already dead - return - raise - -import gtk -import gobject - -class GTKSlave(gobject.GObject,Slave): - __gsignals__ = { - 'operation-complete' : ( - (gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_NO_RECURSE), - gobject.TYPE_NONE, ()), - 'progress-changed' : ( - (gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_NO_RECURSE), - gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_FLOAT)) - } - - def __init__(self, cmd, *args): - gobject.GObject.__init__(self) - Slave.__init__(self,cmd,*args) - self.write_id = None - self.read_id = None - - def on_finish_writing(self): - pass - - - def on_error_readable(self, source, condition): - self.read_stderr() - return True - - def on_readable(self, source, condition): - #print "readable:", source, condition - self.emit('progress-changed', "Reading", -1.0) - self.read() - return True - - def on_writable(self, source, condition): - #print "writable:",source,condition - self.write() - self.emit('progress-changed', "Writing", - (self.in_pos+1.0)/(len(self.input)+1)) - return True - - def remove(self,fd): - try: - gobject.source_remove(fd) - except AttributeError as err: - gtk.input_remove(fd) - - def unregister(self): - if self.write_id: - self.remove(self.write_id) - self.write_id = None - - if self.read_id: - self.remove(self.read_id) - self.read_id = None - - if self.err_id: - self.remove(self.err_id) - self.err_id = None - - def add_read(self,fd,cb): - "Hide differences between PyGTK versions" - try: - return gobject.io_add_watch( - fd, gobject.IO_IN | gobject.IO_HUP, cb) - except AttributeError as err: - return gtk.input_add(fd, gtk.gdk.INPUT_READ, cb) - - def add_write(self,fd,cb): - try: - return gobject.io_add_watch( - fd, gobject.IO_OUT | gobject.IO_HUP, cb) - except AttributeError as err: - return gtk.input_add(fd, gtk.gdk.INPUT_WRITE, cb) - - def register(self): - self.write_id = self.add_write(self.stdin, self.on_writable) - self.read_id = self.add_read(self.stdout, self.on_readable) - self.err_id = self.add_read(self.stderr, self.on_error_readable) - - def on_complete(self): - self.unregister() - - self.emit('progress-changed', "Done",0.0) - self.emit('operation-complete') - - def run(self,input): - Slave.run(self,input) - - self.register() - - def terminate(self): - self.unregister() - Slave.terminate(self) - self.on_complete() - -gobject.type_register(GTKSlave) diff --git a/fractutils/splitugr.py b/fractutils/splitugr.py deleted file mode 100755 index 1d8cf1dc0..000000000 --- a/fractutils/splitugr.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 - -import sys - -from fract4d import translate, fractparser, fractlexer, gradient - -class T: - def __init__(self): - pass - - def translateGradient(self,s): - fractlexer.lexer.lineno = 1 - pt = fractparser.parser.parse(s) - return pt - - def split(self,file): - text = open(file).read() - t = self.translateGradient(text) - for grad in t.children: - t = translate.GradientFunc(grad) - g = gradient.Gradient() - g.load_ugr(t) - out_name = g.name + ".ggr" - f = open("gradients/" + out_name, "w") - print(g.serialize(), file=f) - f.close() - -def main(args): - t = T() - for arg in args: - t.split(arg) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/fractutils/stub_subprocess.py b/fractutils/stub_subprocess.py deleted file mode 100755 index 15c158df2..000000000 --- a/fractutils/stub_subprocess.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -# a minimal script to do nothing, reading in data then printing some out - -import sys -import time - -total_bytes = int(sys.argv[1]) - -if len(sys.argv) > 2: - wait_time = float(sys.argv[2]) -else: - wait_time=0.001 - -bytes_read = 0 -attempts = 0 - -input = "" -while bytes_read < total_bytes and not sys.stdin.closed and attempts < 10000: - data = sys.stdin.read(1000) - bytes_read += len(data) - #print >>sys.stderr, bytes_read - time.sleep(wait_time) - input += data - attempts += 1 - -if attempts == 10000: - #print >>sys.stderr, "gave up" - sys.exit(-1) - - -#print >>sys.stderr, bytes_read -#print >>sys.stderr, "writing to output" - -bytes_written = 0 -while bytes_written < len(input): - bytes_to_write = min(1000, len(input) - bytes_written) - time.sleep(wait_time) - sys.stdout.write(input[bytes_written:bytes_written + bytes_to_write]) - bytes_written += bytes_to_write - #print >>sys.stderr, "wrote %d" % bytes_written - -#print >>sys.stderr, "DONE" - -sys.stdout.flush() -sys.stdout.close() - -#print >>sys.stderr, "double DONE" -#sys.exit(0) diff --git a/fractutils/tattered.jpg b/fractutils/tattered.jpg deleted file mode 100644 index d5120bfac..000000000 Binary files a/fractutils/tattered.jpg and /dev/null differ diff --git a/fractutils/tattered.png b/fractutils/tattered.png deleted file mode 100644 index 61a953112..000000000 Binary files a/fractutils/tattered.png and /dev/null differ diff --git a/fractutils/test.py b/fractutils/test.py deleted file mode 100755 index 188ebf53e..000000000 --- a/fractutils/test.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -import unittest - -#import test_makemap -import test_slave - -def suite(): - tests = ( - #test_makemap.suite(), - test_slave.suite(), - ) - return unittest.TestSuite(tests) - -def main(): - unittest.main(defaultTest='suite') - -def profile(): - import hotshot - prof = hotshot.Profile("makemap.prof") - prof.runcall(main) - prof.close() - -if __name__ == '__main__': - main() - diff --git a/fractutils/test000.png b/fractutils/test000.png deleted file mode 100644 index 0078ab0e0..000000000 Binary files a/fractutils/test000.png and /dev/null differ diff --git a/fractutils/test001.png b/fractutils/test001.png deleted file mode 100644 index 251662774..000000000 Binary files a/fractutils/test001.png and /dev/null differ diff --git a/fractutils/test002.png b/fractutils/test002.png deleted file mode 100644 index f377a36fd..000000000 Binary files a/fractutils/test002.png and /dev/null differ diff --git a/fractutils/test_formdb.py b/fractutils/test_formdb.py deleted file mode 100755 index f2f0bc804..000000000 --- a/fractutils/test_formdb.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 - -# this is deliberately not included in test.py since it hits a live website -# and I don't want to screw up their bandwidth allocation - -import unittest -import io -import socketserver -import http.server -import posixpath -import urllib.request, urllib.parse, urllib.error -import http.client -import os -import threading, time -import zipfile -import sys - -if sys.path[1] != "..": sys.path.insert(1, "..") - -from fractutils import slave - -from fract4d import formdb - -# Rather than hassle the real UF formula DB when running unit tests, -# we run a temporary fake web server -formdb.target_base = "http://localhost:8090/" - -class MyRequestHandler(http.server.SimpleHTTPRequestHandler): - # same as a simplehttprequesthandler, but with a different base dir - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - path = posixpath.normpath(urllib.parse.unquote(path)) - words = path.split('/') - words = [_f for _f in words if _f] - path = posixpath.normpath("../testdata") - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - if word in (os.curdir, os.pardir): continue - path = os.path.join(path, word) - return path - - #def log_message(self, format, *args): - # # hide log messages - # pass - -def threadStart(): - handler = MyRequestHandler - httpd = socketserver.TCPServer(("",8090), handler) - httpd.serve_forever() - -thread = threading.Thread(target=threadStart) -thread.setDaemon(True) -thread.start() -# hack - make sure local server has started before running tests -time.sleep(0.5) - -class Test(unittest.TestCase): - def testFetch(self): - conn = http.client.HTTPConnection('localhost',8090) - conn.request("GET", "/trigcentric.fct") - response = conn.getresponse() - self.assertEqual(200, response.status) - - def testFetchWithFormDB(self): - slave = formdb.beginFetchZip("test.zip") - self.assertEqual("http://localhost:8090/test.zip", url) - - def testFetchAndUnpack(self): - conn = http.client.HTTPConnection('localhost',8090) - conn.request("GET", "/test.zip") - response = conn.getresponse() - self.assertEqual(200, response.status) - zf = zipfile.ZipFile(io.StringIO(response.read()),"r") - info = zf.infolist() - self.assertEqual("trigcentric.fct", info[0].filename) - - def testCreate(self): - f = open("../testdata/example_formula_db.txt") - - formlinks = formdb.parse(f) - - self.assertEqual(5, len(formlinks)) - - self.assertEqual( - "/cgi-bin/formuladb?view;file=gwfa.ucl;type=.txt", formlinks[0]) - -def suite(): - return unittest.makeSuite(Test,'test') - -if __name__ == '__main__': - unittest.main(defaultTest='suite') - diff --git a/fractutils/test_makemap.py b/fractutils/test_makemap.py deleted file mode 100755 index 57998ceb9..000000000 --- a/fractutils/test_makemap.py +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env python - -import unittest -import sys - -if sys.path[1] != "..": sys.path.insert(1, "..") - -from fractutils import makemap - -from fract4d import gradient - -class Test(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - pass - - def testAllocate(self): - mm = makemap.T() - - self.assertEqual(None,mm.root) - - def testAddOnePixel(self): - mm = makemap.T() - mm.insertPixel(0,0,0) - n = mm.root - self.assertEqual((0,0,0,1), (n.r, n.g, n.b, n.count)) - - def testAddOnePixelTwice(self): - mm = makemap.T() - mm.insertPixel(0,0,0) - mm.insertPixel(0,0,0) - n = mm.root - self.assertEqual((0,0,0,2), (n.r, n.g, n.b, n.count)) - - def testAddTwoPixels(self): - mm = makemap.T() - mm.insertPixel(0,0,0) - mm.insertPixel(0xff,0xff,0xff) - self.checkTwoPixels(mm) - - def testAddTwoPixelsReversed(self): - mm = makemap.T() - mm.insertPixel(0xff,0xff,0xff) - mm.insertPixel(0,0,0) - self.checkTwoPixels(mm) - - def checkTwoPixels(self,mm): - n = mm.root - self.assertEqual((127,127,127,0), (n.r, n.g, n.b, n.count)) - n1 = n.branches[0] - self.assertEqual((0,0,0,1), (n1.r, n1.g, n1.b, n1.count)) - n2 = n.branches[7] - self.assertEqual((0xff,0xff,0xff,1), (n2.r, n2.g, n2.b, n2.count)) - - def testAddThreePixels(self): - mm = makemap.T() - mm.insertPixel(0xff,0xff,0xff) - mm.insertPixel(0,0,0) - mm.insertPixel(0xff,0x00,0x00) - self.checkThreePixels(mm) - - def checkThreePixels(self,mm): - self.checkTwoPixels(mm) - n3 = mm.root.branches[4] - self.assertEqual((0xff,0x00,0x00,1), (n3.r, n3.g, n3.b, n3.count)) - - def testAddTwoPixelsToSameBranch(self): - mm = makemap.T() - mm.insertPixel(0xff,0xff,0xff) - mm.insertPixel(0x80,0x80,0x80) - r = mm.root - self.assertEqual(False,r.isleaf()) - b = r.branches[7] - self.assertEqual(False,b.isleaf()) - - def testAddTwoVerySimilarPixels(self): - mm = makemap.T() - mm.insertPixel(0xff,0xff,0xff) - mm.insertPixel(0xff,0xff,0xfe) - r = mm.root - depth=0 - n = 127 - size = 64 - while not r.branches[7].isleaf(): - self.assertEqual((r.r,r.g,r.b),(n,n,n)) - n = n + size - size = size / 2 - depth += 1 - r = r.branches[7] - self.assertEqual(7, depth) - self.assertEqual(False, r.isleaf()) - self.assertEqual((0xfe,0xfe,0xfe,0), (r.r, r.g, r.b, r.count)) - leaf1 = r.branches[6] - self.assertEqual((0xff,0xff,0xfe,1), - (leaf1.r, leaf1.g, leaf1.b, leaf1.count)) - leaf2 = r.branches[7] - self.assertEqual((0xff,0xff,0xff,1), - (leaf2.r, leaf2.g, leaf2.b, leaf2.count)) - - - def testNode(self): - n = makemap.Node(0,0,0,0) - self.assertEqual([None]*8, n.branches) - - self.assertEqual(True, n.isleaf()) - - def testLoadPicture(self): - 'Load a very simple test image and check correctly parsed' - mm = makemap.T() - mm.load(open("test000.png","rb")) - - seq = mm.getdata() - self.assertEqual(len(seq),10 * 10) - i = 0 - for pix in seq: - if i % 10 < 5: - # left half of image is black - self.assertEqual(pix,(0,0,0)) - else: - # right half is white - self.assertEqual(pix,(255,255,255)) - i+= 1 - - mm.build() - r = mm.root - self.assertEqual(False, r.isleaf()) - blackNode = r.branches[0] - whiteNode = r.branches[7] - self.assertEqual(blackNode.count,50) - self.assertEqual(whiteNode.count,50) - - def testCollapseNode(self): - mm = makemap.T() - # 2 black + 1 white pixels - mm.insertPixel(0xff,0xff,0xff) - mm.insertPixel(0x00,0x00,0x00) - mm.insertPixel(0x00,0x00,0x00) - - expected_err = 0xFF**2 * 3 - self.assertEqual(expected_err, mm.get_collapse_error(mm.root)) - - candidates = mm.find_collapse_candidates(mm.root,[]) - self.assertEqual(1, len(candidates)) - self.assertEqual( - [ (expected_err, mm.root)], candidates) - - mm.collapse(mm.root) - - # should have 1 leaf containing all 3 pixels using - # most popular color - self.assertEqual(True, mm.root.isleaf()) - self.assertEqual(3, mm.root.count) - self.assertEqual((0,0,0), (mm.root.r, mm.root.g, mm.root.b)) - - def testCollapseNode2(self): - mm = makemap.T() - # 2 black, 1 white, 1 dark gray pixels - mm.insertPixel(0xff,0xff,0xff) - mm.insertPixel(0x00,0x00,0x00) - mm.insertPixel(0x00,0x00,0x00) - mm.insertPixel(0x01,0x01,0x01) - - while True: - candidates = mm.find_collapse_candidates(mm.root,[]) - self.assertEqual(1, len(candidates)) - (err,c) = candidates[0] - if c == mm.root: - break - - self.assertTrue(err <= 1*3, err) - mm.collapse(c) - - self.assertEqual(0xFF**2 * 3, err) - - def testReduceColors(self): - mm = makemap.T() - self.assertEqual(0,mm.numColors()) - - # 2 black, 1 white, 1 dark gray pixels - mm.insertPixel(0xff,0xff,0xff) - self.assertEqual(1,mm.numColors()) - mm.insertPixel(0x00,0x00,0x00) - self.assertEqual(2,mm.numColors()) - mm.insertPixel(0x00,0x00,0x00) - self.assertEqual(2,mm.numColors()) - mm.insertPixel(0x01,0x01,0x01) - self.assertEqual(3,mm.numColors()) - - colors = mm.colors() - colors.sort() - self.assertEqual( - [(0x00,0x00,0x00), (0x01,0x01,0x01), (0xff,0xff,0xff)], - colors) - - mm.reduceColors(2) - self.assertEqual(2,mm.numColors()) - - colors = mm.colors() - colors.sort() - self.assertEqual( - [(0x00,0x00,0x00), (0xff,0xff,0xff)], - colors) - - def testReduceColors2(self): - mm = makemap.T() - in_colors = [] - for i in range(256): - in_colors.append((i,0,0)) - for j in range(i+1): - mm.insertPixel(i,0,0) - self.assertEqual(256,mm.numColors()) - - mm.reduceColors(255) - colors = mm.colors() - colors.sort() - self.assertEqual(in_colors[1:],colors) - - mm.reduceColors(254) - colors = mm.colors() - colors.sort() - # 0 and 2 are removed - self.assertEqual([(1,0,0)] + in_colors[3:],colors) - - mm.reduceColors(3) - - def testRealImage(self): - mm = makemap.T() - mm.load(open("1453558084_8bb56e8b82_t.jpg")) - mm.build(100) - - mm.reduceColors(10) - - grad = gradient.Gradient() - - colors = [] - i = 0 - for (r,g,b) in mm.colors(): - colors.append((i/10.0,r,g,b,255)) - i += 1 - - grad.load_list(colors) - grad.save(open("../maps/test.ggr","w")) - -def suite(): - return unittest.makeSuite(Test,'test') - -if __name__ == '__main__': - unittest.main(defaultTest='suite') - diff --git a/fractutils/test_parfile.py b/fractutils/test_parfile.py deleted file mode 100755 index 3ed01d113..000000000 --- a/fractutils/test_parfile.py +++ /dev/null @@ -1,429 +0,0 @@ -#!/usr/bin/env python3 - -# test cases for parfile.py - -import string -import unittest -import io -import math -import sys - -if sys.path[1] != "..": sys.path.insert(1, "..") - -from fract4d_compiler import fc, preprocessor -from fract4d import testbase, fractal, fractconfig, gradient -from fractutils import parfile - -g_comp = fc.Compiler(fractconfig.T("")) -g_comp.add_func_path("../formulas") -g_comp.add_func_path("../fract4d") -g_comp.load_formula_file("gf4d.frm") -g_comp.load_formula_file("test.frm") -g_comp.load_formula_file("gf4d.cfrm") - -fotd = """FOTD_for_04-05-06 { ; time=2:38:57.82--SF5 on a P200 -reset=2004 type=mandel passes=1 -center-mag=-0.74999655467724592903865/0.0171269216\\ -3034049041486/5.51789e+018 params=0/0 float=y -maxiter=72000 inside=0 periodicity=10 -colors=0000qJ0mP0iX0eb0di0`o0Xu0Tz2Pz2NzRTzoZqzbRz\\ -dTzdTzeTzeTzeTzgVzgVzgVziVziVzkXzkXzkXzmXzmXzmXzgV\\ -zdTu`RkXP`TNRNNGJL6GJ0CH08K04U0GcAWdPdkehvpmuxrzzx\\ -zzzuzzqzzmzzizzezzbzzZzzVzzTzzRzzRxzPozPexNZvNPsLG\\ -qL8oLEkNJiNNgNTeNXdN``PbXPeTPgPPkLPmHPqEPsARv6Rx2R\\ -z0Rz0Rz0Rz0RzzLzzPzgTzLXz0`z0Xz0Vz0Rz0Pz0Lz0Jz0Gz0\\ -Ez0Az08z04z02z00z00z00s2GNV`0uu0so6qiCodJo`PmVXkPd\\ -kLiiGqgAvg6Lzb0zz2zgJzJ`z0Tz2Nz8HzECxJ6vP0sV0q`0me\\ -0kk0io0os0su0xx4zzCzzHzzPzzVzzXzzXzzXzzXzzXzzXzxXx\\ -vXvuXssXqqXmoXkmbizVdzPZyHTsCNh6HZ0CS06Q00S00U00W0\\ -0Y00_00a40cC4eJ6hR8kZAneEqmGtuHwzJzzLzz0Hz0Gz0Gz0E\\ -z0Ez0Ez0Cz0Cz0Cz0Ax0Av08u08q08o06m06k06s0Cgz0TZzz0\\ -zz0zz2zq6ziAz`GzRJxHNvARuLGko0CV4de0Vo0NgVzkHzo6ss\\ -0ev0TmCbq2Xs0Tu0Nv0J0z02z0Cs0Lb0XL2e46o0CiZpoRmqGe\\ -c4`h0Tc0LGzzRdxbHim0VPJob2bm0R60AP0Cg0EzzzzzzzdqxH\\ -dx0RzzLzmJzNHx0G0z00v60uE }""" - -fotd2 = """FOTD_for_02-05-06 { ; time=0:03:43.11--SF5 on a P200 -reset=2004 type=formula formulafile=allinone.frm -formulaname=MandelbrotMix4 function=ident passes=1 -center-mag=+0.27710097748940720/+2.663965799618463\ -00/5.609397e+008/1/22.5/1.57466375611675646e-005 -params=-1/1.5/1/-1.5/0/0 float=y maxiter=750 -inside=0 logmap=69 periodicity=10 -colors=0000HG5JA9L6VNRlCeJC0JJRJCnJ6z`Uzokzuuuzzzf\ -bjqaxkTreHn_6i`9oaAtbCzbCz_NzVUzR`yNhwHnvCss9yrozz\ -rpzvfzyXzzLzz6yzGwzOvzUsz`ryepxkowpntnqrlspkwniykh\ -zifzhezdiq`laXpJUr0GbsLawO`yR_zUYzYXz`VzbUzeUzkVzq\ -XzwYzz_zz`zzazsiw_paVrbTtbQwbLybHzbEzbOqhVdlaQpYXi\ -UaaRhUNlLJqAz5bz2fz2iz2kz0nz0pw0rt0ts6ssCsrGrrLrrQ\ -rqTqqXqq_qlYjhXbbVXYUOTTGNR5HQ0LTANUNOXXRYeT`nUavV\ -bzU_zTXzRUzRRzQOzOLzNHyNEyXHweLvlOstRrzUpzVox_jnae\ -bdaQfXAiR0kN0nH5pCAr6Gs2Lk5Qb6TV9XLA`9Cb0CVCAOOAE_\ -A5hACiOJiYQifVjp`jxejzohqxebzbNza0o_jJXzYiyishszOq\ -vRpnUneXlX_jNaiAdh0feE`dTVadQ`oJYyCXz5TnGalRjk`rji\ -ziqziytjYhk0jiCkfQle`niVnkQnnJnpCnr5nt0fo6_iGRbOHX\ -V9RaTHjf9rs0yvftwzpsznpzklziizfezdazarzwozxlzxizxf\ -zybzy`zyYzyVzzTzzQzzNzzJzzHzzGzzEzzCztAzl9ze6zYzzQ\ -zzzzzyzzqzzhzz_zzkzzvzzzzzijzelzazzzzzzzzzzzzzzzzz\ -zzzzvzxozsizpazk0zR0zO0zL } -""" - -map="""0 0 0 - 0 68 64 - 20 76 40 - 36 84 24 -124 92 108 -196 48 168 - 76 48 0 - 76 76 108 - 76 48 204 - 76 24 252 -148 120 252 -208 192 252 -232 232 232 -252 252 252 -172 156 188 -216 152 244 -192 116 220 -168 68 204 -144 24 184 -148 36 208 -152 40 228 -156 48 252 -156 48 252 -144 92 252 -124 120 252 -108 148 248 - 92 180 240 - 68 204 236 - 48 224 224 - 36 248 220 -208 252 252 -220 212 252 -236 172 252 -248 132 252 -252 84 252 -252 24 248 -252 64 240 -252 96 236 -252 120 224 -252 148 220 -248 168 212 -244 192 208 -240 212 204 -228 204 216 -220 196 224 -212 192 240 -204 184 248 -192 180 252 -184 172 252 -180 168 252 -164 184 216 -148 196 152 -132 212 76 -120 220 0 - 64 156 224 - 84 152 240 - 96 148 248 -108 144 252 -120 136 252 -136 132 252 -148 124 252 -156 120 252 -168 120 252 -192 124 252 -216 132 252 -240 136 252 -252 144 252 -252 148 252 -252 152 252 -224 184 240 -144 212 152 -124 220 156 -116 228 156 -104 240 156 - 84 248 156 - 68 252 156 - 56 252 156 - 96 216 180 -124 164 196 -152 104 212 -136 132 184 -120 152 152 -108 180 120 - 92 196 84 - 76 216 40 -252 20 156 -252 8 172 -252 8 184 -252 8 192 -252 0 204 -252 0 212 -240 0 220 -228 0 228 -224 24 224 -224 48 224 -220 64 220 -220 84 220 -220 104 220 -216 116 216 -216 132 216 -216 144 216 -196 136 188 -180 132 156 -156 124 132 -136 120 96 -116 116 64 - 92 108 20 - 68 104 0 - 84 116 40 - 92 120 92 - 96 132 132 -108 136 168 -116 148 204 -120 152 236 -124 156 252 -120 144 252 -116 132 252 -108 120 252 -108 108 252 -104 96 252 - 96 84 252 - 92 68 248 - 92 56 248 -132 68 240 -168 84 236 -196 96 224 -228 108 220 -252 120 212 -252 124 208 -244 144 188 -204 152 168 -156 164 152 -104 172 132 - 40 184 108 - 0 192 92 - 0 204 68 - 20 212 48 - 40 220 24 - 64 224 8 - 84 192 20 -104 156 24 -116 124 36 -132 84 40 -148 36 48 -156 0 48 -124 48 40 - 96 96 40 - 56 144 40 - 20 180 40 - 48 184 96 - 76 184 136 -104 184 172 -124 188 212 -148 188 244 -168 188 252 -208 180 216 -244 168 156 -252 156 92 -252 152 0 -208 144 188 - 76 132 252 -136 184 248 -184 224 180 -224 252 96 -216 236 108 -212 204 120 -204 168 132 -196 132 144 -188 92 152 -184 40 164 -180 0 172 -168 56 148 -164 116 124 -152 164 104 -148 208 76 -136 248 48 -132 252 20 -116 204 64 -152 196 108 -188 192 148 -220 188 184 -252 184 216 -252 184 248 -228 188 136 -180 192 0 -188 184 48 -192 172 104 -196 168 148 -204 184 124 -204 192 104 -204 204 76 -204 212 48 -204 220 20 -204 228 0 -172 208 24 -144 184 64 -108 156 96 - 68 132 124 - 36 108 152 -116 68 188 -172 36 220 -224 0 248 -236 172 228 -240 252 212 -224 252 204 -212 252 192 -196 252 184 -184 252 172 -168 252 164 -152 252 152 -220 252 240 -208 252 244 -196 252 244 -184 252 244 -172 252 248 -156 252 248 -148 252 248 -136 252 248 -124 252 252 -116 252 252 -104 252 252 - 92 252 252 - 76 252 252 - 68 252 252 - 64 252 252 - 56 252 252 - 48 252 228 - 40 252 196 - 36 252 168 - 24 252 136 -252 252 104 -252 252 252 -252 252 248 -252 252 216 -252 252 180 -252 252 144 -252 252 192 -252 252 236 -252 252 252 -252 252 184 -188 252 168 -196 252 152 -252 252 252 -252 252 252 -252 252 252 -252 252 252 -252 252 252 -252 252 252 -252 252 252 -236 252 244 -208 252 224 -184 252 212 -152 252 192 - 0 252 108 - 0 252 96 - 0 252 84 -""" - -class ParTest(testbase.TestBase): - def setUp(self): - pass - - def tearDown(self): - pass - - def testColors(self): - self.assertEqual(parfile.colorRange(""), []) - - colors = parfile.colorRange("05F") - self.assertEqual(len(colors),1) - - colors = parfile.colorRange("05F<63>DISDISDIS<89>uxxuxxvyyvyywzz<94>05F") - self.assertEqual(len(colors),256) - - self.assertRaises(RuntimeError,parfile.colorRange, "&*(") - self.assertRaises(RuntimeError,parfile.colorRange, "00") - self.assertRaises(RuntimeError,parfile.colorRange, "000<0>000") - self.assertRaises(RuntimeError,parfile.colorRange, "<1>000") - self.assertRaises(RuntimeError,parfile.colorRange, "") - - def testColors2(self): - colors = parfile.colorRange("0000HG5JA9L6VNRlCeJC0JJRJCnJ6z`UzokzuuuzzzfbjqaxkTreHn_6i`9oaAtbCzbCz_NzVUzR`yNhwHnvCss9yrozzrpzvfzyXzzLzz6yzGwzOvzUsz`ryepxkowpntnqrlspkwniykhzifzhezdiq`laXpJUr0GbsLawO`yR_zUYzYXz`VzbUzeUzkVzqXzwYzz_zz`zzazsiw_paVrbTtbQwbLybHzbEzbOqhVdlaQpYXiUaaRhUNlLJqAz5bz2fz2iz2kz0nz0pw0rt0ts6ssCsrGrrLrrQrqTqqXqq_qlYjhXbbVXYUOTTGNR5HQ0LTANUNOXXRYeT`nUavVbzU_zTXzRUzRRzQOzOLzNHyNEyXHweLvlOstRrzUpzVox_jnaebdaQfXAiR0kN0nH5pCAr6Gs2Lk5Qb6TV9XLA`9Cb0CVCAOOAE_A5hACiOJiYQifVjp`jxejzohqxebzbNza0o_jJXzYiyishszOqvRpnUneXlX_jNaiAdh0feE`dTVadQ`oJYyCXz5TnGalRjk`rjiziqziytjYhk0jiCkfQle`niVnkQnnJnpCnr5nt0fo6_iGRbOHXV9RaTHjf9rs0yvftwzpsznpzklziizfezdazarzwozxlzxizxfzybzy`zyYzyVzzTzzQzzNzzJzzHzzGzzEzzCztAzl9ze6zYzzQzzzzzyzzqzzhzz_zzkzzvzzzzzijzelzazzzzzzzzzzzzzzzzzzzzzvzxozsizpazk0zR0zO0zL") - - check_grad = gradient.Gradient() - check_grad.load_map_file(io.StringIO(map),-1) - - for(thecolor,expcolor) in zip(colors[1:],check_grad.segments): - self.assertEqual(thecolor[0],int(255*expcolor.left_color[0])) - self.assertEqual(thecolor[1],int(255*expcolor.left_color[1])) - self.assertEqual(thecolor[2],int(255*expcolor.left_color[2])) - - def testLoadFOTD1(self): - fotd_file = io.StringIO(fotd) - f = fractal.T(g_comp) - parfile.parse(fotd_file, f) - self.assertEqual(f.maxiter, 72000) - - def testGetParams(self): - fotd_file = io.StringIO(fotd) - - params = parfile.get_params(fotd_file) - self.assertEqual(len(params), 18) - self.assertEqual(params[0],"FOTD_for_04-05-06") - - def testGetPairs(self): - fotd_file = io.StringIO(fotd) - - params = parfile.get_params(fotd_file) - pairs = parfile.get_param_pairs(params) - - self.assertEqual(pairs["maxiter"],'72000') - - def testParseParams(self): - f = fractal.T(g_comp) - parfile.parse_maxiter("72000",f) - self.assertEqual(f.maxiter,72000) - parfile.parse_colors('0000qJ0mP0iX0eb0di0`o0Xu0Tz2Pz2NzRTzoZqzbRzdTzdTzeTzeTzeTzgVzgVzgVziVziVzkXzkXzkXzmXzmXzmXzgVzdTu`RkXP`TNRNNGJL6GJ0CH08K04U0GcAWdPdkehvpmuxrzzxzzzuzzqzzmzzizzezzbzzZzzVzzTzzRzzRxzPozPexNZvNPsLGqL8oLEkNJiNNgNTeNXdN``PbXPeTPgPPkLPmHPqEPsARv6Rx2Rz0Rz0Rz0Rz0RzzLzzPzgTzLXz0`z0Xz0Vz0Rz0Pz0Lz0Jz0Gz0Ez0Az08z04z02z00z00z00s2GNV`0uu0so6qiCodJo`PmVXkPdkLiiGqgAvg6Lzb0zz2zgJzJ`z0Tz2Nz8HzECxJ6vP0sV0q`0me0kk0io0os0su0xx4zzCzzHzzPzzVzzXzzXzzXzzXzzXzzXzxXxvXvuXssXqqXmoXkmbizVdzPZyHTsCNh6HZ0CS06Q00S00U00W00Y00_00a40cC4eJ6hR8kZAneEqmGtuHwzJzzLzz0Hz0Gz0Gz0Ez0Ez0Ez0Cz0Cz0Cz0Ax0Av08u08q08o06m06k06s0Cgz0TZzz0zz0zz2zq6ziAz`GzRJxHNvARuLGko0CV4de0Vo0NgVzkHzo6ss0ev0TmCbq2Xs0Tu0Nv0J0z02z0Cs0Lb0XL2e46o0CiZpoRmqGec4`h0Tc0LGzzRdxbHim0VPJob2bm0R60AP0Cg0EzzzzzzzdqxHdx0RzzLzmJzNHx0G0z00v60uE',f) - self.assertEqual(len(f.get_gradient().segments),255) - - parfile.parse_center_mag("-0.74999655467724592903865/0.01712692163034049041486/5.51789e+018",f) - self.assertEqual(f.params[f.XCENTER],-0.74999655467724592903865) - self.assertEqual(f.params[f.YCENTER],-0.01712692163034049041486) - self.assertEqual(f.params[f.MAGNITUDE],2.0/5.51789e+018 * 1.33) - - def disabled_testLogTableLimits(self): - # disabled since for some odd reason we get a slightly different - # logtable from Fractint - (lf,mlf) = parfile.get_log_table_limits(69,750,256,2004) - self.assertEqual(69,lf) - self.assertNearlyEqual([38.935782028258387], [mlf]) - - n = parfile.calc_log_table_entry(0,69,lf,mlf, 2004) - self.assertEqual(1 ,n) - - n = parfile.calc_log_table_entry(69,69,lf,mlf, 2004) - self.assertEqual(1 ,n) - - n = parfile.calc_log_table_entry(70,69,lf,mlf, 2004) - self.assertEqual(1 ,n) - - n = parfile.calc_log_table_entry(71,69,lf,mlf, 2004) - self.assertEqual(2 ,n) - - # this gets the wrong answer - n = parfile.calc_log_table_entry(749,69,lf,mlf, 2004) - self.assertEqual(0xfd,n) - - def testSetupLogTable(self): - table = parfile.setup_log_table(69, 750, 256, 2004) - self.assertEqual(750,len(table)) - - self.assertEqual([1] * 70, table[0:70]) # all entries <= logflag should be 1 - -def suite(): - return unittest.makeSuite(ParTest,'test') - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff --git a/fractutils/test_slave.py b/fractutils/test_slave.py deleted file mode 100755 index db060f09d..000000000 --- a/fractutils/test_slave.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python - -# check that running a slow out-of-proc piece of code in a cancellable way works - -import unittest -import select -import sys - -import gtk - -if sys.path[1] != "..": sys.path.insert(1, "..") - -from fractutils import slave - -class GTKTestSlave(object): - def __init__(self, cmd, *args): - self.s = slave.GTKSlave(cmd,*args) - self.complete = False - - window = gtk.Window() - self.bar = gtk.ProgressBar() - window.add(self.bar) - window.show_all() - - self.s.connect('progress-changed',self.on_progress) - self.s.connect('operation-complete', self.on_complete) - - def on_complete(self,slave): - self.complete = True - gtk.main_quit() - - def on_progress(self,slave,type,position): - if position == -1.0: - self.bar.pulse() - else: - self.bar.set_fraction(position) - self.bar.set_text(type) - return True - - def run(self,input): - self.s.run(input) - - gtk.main() - - -class Test(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - pass - - def runProcess(self,input,wait_time): - s = slave.Slave("./stub_subprocess.py", str(wait_time)) - - s.run(input) - return s - - def sendInput(self,s,n=sys.maxsize): - while n > 0: - (r,w,e) = select.select([],[s.stdin],[],0.01) - if len(w) == 0: - continue - - if not s.write(): - break - - n -= 1 - #print "done with input" - - def readOutput(self,s): - bytes_read = 0 - while 1: - (r,w,e) = select.select([s.stdout],[],[],0.01) - if len(r) == 0: - continue - - if not s.read(): - break - - def testTerminate(self): - s = self.runProcess("y" * 200, 2.0) - self.sendInput(s,1) - s.terminate() - self.assertEqual(False, s.write()) - self.assertEqual(False, s.read()) - - def testRun(self): - input = "x" * (100 * 1000) - s = self.runProcess(input, 0.001) - self.sendInput(s) - self.readOutput(s) - self.assertEqual(input, s.output) - - def testNoSuchProcess(self): - s = GTKTestSlave("./xxx.py", str(0.01)) - input = "x" * 100 - self.assertRaises(OSError, s.run, input) - - def testRegister(self): - s = GTKTestSlave("./stub_subprocess.py", str(0.01)) - - input = "x" * (100 * 1000) - s.run(input) - self.assertEqual(input, s.s.output) - self.assertTrue(s.complete, "operation didn't complete") - - def testGet(self): - s = GTKTestSlave("./get.py", "GET", "http://www.google.com/index.html" ) - s.run("") - if "Name or service not known" in s.s.err_output: - # we don't have a network connection so can't try this - return - self.assertTrue(s.s.output.count("oogle") > 0) - - def testGetBadSite(self): - s = GTKTestSlave( - "./get.py", "GET", "http://www.sdgsdvsdvsdvsdbbbs.com/index.html" ) - s.run("") - self.assertNotEqual(s.s.process.returncode,0) - self.assertTrue(s.s.err_output.count('Name or service not known') > 0) - - def testGetBadPage(self): - s = GTKTestSlave( - "./get.py", "GET", "http://www.google.com/blahblahblah.html" ) - s.run("") - self.assertEqual(s.s.process.returncode,1) - if "Name or service not known" in s.s.err_output: - # we don't have a network connection so can't try this - return - - self.assertTrue(s.s.err_output.count('404: Not Found') > 0) - - -def suite(): - return unittest.makeSuite(Test,'test') - -if __name__ == '__main__': - unittest.main(defaultTest='suite') - - - diff --git a/manual/config.toml b/manual/config.toml index 8a566d183..8518434bc 100644 --- a/manual/config.toml +++ b/manual/config.toml @@ -30,7 +30,7 @@ enableGitInfo = true [params] # Gnofract 4D version - version = "4.2" + version = "4.3" # Used to customize the location of the stylesheet, which need to be different # for local installation and for use on the website diff --git a/setup.py b/setup.py index 6183ad387..237f9f65f 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ import subprocess import sys -gnofract4d_version = '4.2' +gnofract4d_version = '4.3' _DEBUG = False @@ -131,6 +131,7 @@ def call_package_config(package, option, optional=False): ) + def get_files(dir, ext): return [os.path.join(dir, x) for x in os.listdir(dir) if x.endswith(ext)] @@ -138,8 +139,8 @@ def get_files(dir, 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'])) + icons.append(('share/icons/hicolor/{0}x{0}/apps'.format(size), + ['pixmaps/logo/{0}x{0}/gnofract4d.png'.format(size)])) return icons diff --git a/test.py b/test.py index 1b75c9ecf..918934781 100755 --- a/test.py +++ b/test.py @@ -11,11 +11,12 @@ from fract4d import options + class Test(unittest.TestCase): def testSetupPyVersionMatches(self): - doc = open("setup.py") - content = doc.read() - doc.close() + with open("setup.py") as doc: + content = doc.read() + doc_re = re.compile(r"gnofract4d_version = '(\S+)'") m = doc_re.search(content) @@ -24,9 +25,8 @@ def testSetupPyVersionMatches(self): def testDocVersionMatches(self): # check the docs - doc = open("manual/config.toml") - content = doc.read() - doc.close() + with open("manual/config.toml") as doc: + content = doc.read() ver_re = re.compile(r'version = "(\S+)"') @@ -34,13 +34,24 @@ def testDocVersionMatches(self): self.assertTrue(m, "manual doesn't specify version") self.assertEqual(options.VERSION, m.group(1), "Version mismatch") + def testReadmeVersionMatches(self): + with open("README.md") as doc: + content = doc.read() + + ver_re = re.compile(r'gnofract4d-(\S+)\.tar\.gz') + + m = ver_re.search(content) + self.assertTrue(m, "README doesn't specify version") + self.assertEqual(options.VERSION, m.group(1), "Version mismatch") + def testWebsiteVersionMatches(self): if not os.path.exists("website"): # not included in source dist return - mkweb = open("website/content/_index.md") - content = mkweb.read() - mkweb.close() + + with open("website/content/_index.md") as doc: + content = doc.read() + ver_re = re.compile(r'latest: "(\S+)"') m = ver_re.search(content) @@ -58,8 +69,10 @@ def testGenerateMandelbrot(self): "--width", "24", "-j", "12", "-q"], check=True) self.assertTrue(os.path.exists(test_file)) + def suite(): return unittest.makeSuite(Test, 'test') + if __name__ == '__main__': unittest.main(defaultTest='suite') diff --git a/website/content/_index.md b/website/content/_index.md index 03fc2b38f..64eb7c96f 100644 --- a/website/content/_index.md +++ b/website/content/_index.md @@ -2,7 +2,7 @@ title: "Front Page" date: 2020-04-12T08:55:54-07:00 draft: false -latest: "4.2" +latest: "4.3" ---