From 0896ba96e6b9a6f85fa48eb0b61b209f3425e62a Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Thu, 22 Dec 2016 19:42:21 -0800 Subject: [PATCH] else --- AESFile/aesfile.py | 30 +- Downloady/downloady.py | 75 +- Pathclass/pathclass.py | 125 +- Safeprint/safeprint.py | 18 + ServerReference/simpleserver.py | 2 +- Steganographic/asciibet.txt | 2519 +++++++++--------- Templates/timetest.py | 22 + ThreadedDL/threaded_dl.py | 4 + Toolbox/brename.py | 4 +- Toolbox/fileswith.py | 3 +- Toolbox/touch.py | 6 +- VoxelSphereGenerator/dot_corner.png | Bin 0 -> 168 bytes VoxelSphereGenerator/dot_highlight.png | Bin 0 -> 168 bytes VoxelSphereGenerator/dot_normal.png | Bin 0 -> 168 bytes VoxelSphereGenerator/voxelspheregenerator.py | 274 ++ WebstreamZip/README.md | 3 + WebstreamZip/test.zip | Bin 10240 -> 0 bytes WebstreamZip/tester.py | 7 - _voussoirkit/voussoirkit.py | 3 +- _voussoirkit/voussoirkit.zip | Bin 16921 -> 18405 bytes 20 files changed, 1826 insertions(+), 1269 deletions(-) create mode 100644 Safeprint/safeprint.py create mode 100644 Templates/timetest.py create mode 100644 VoxelSphereGenerator/dot_corner.png create mode 100644 VoxelSphereGenerator/dot_highlight.png create mode 100644 VoxelSphereGenerator/dot_normal.png create mode 100644 VoxelSphereGenerator/voxelspheregenerator.py create mode 100644 WebstreamZip/README.md delete mode 100644 WebstreamZip/test.zip delete mode 100644 WebstreamZip/tester.py diff --git a/AESFile/aesfile.py b/AESFile/aesfile.py index a44b3c9..c63d396 100644 --- a/AESFile/aesfile.py +++ b/AESFile/aesfile.py @@ -9,10 +9,24 @@ import os from voussoirkit import bytestring +# 128 bits BLOCK_SIZE = 32 - +KEY_SIZE = 32 SEEK_END = 2 +def decrypt_data(aes, data): + data = aes.decrypt(data) + pad_byte = data[-1:] + data = data.rstrip(pad_byte) + return data + +def encrypt_data(aes, data): + pad_byte = (data[-1] + 1) % 256 + pad_length = BLOCK_SIZE - (len(data) % BLOCK_SIZE) + data += (bytes([pad_byte]) * pad_length) + data = aes.encrypt(data) + return data + def decrypt_file(aes, input_handle, output_handle): current_pos = input_handle.tell() input_size = input_handle.seek(0, SEEK_END) - current_pos @@ -42,8 +56,7 @@ def encrypt_file(aes, input_handle, output_handle): last_byte = chunk[-1] if len(chunk) < BLOCK_SIZE: pad_byte = (last_byte + 1) % 256 - pad_byte = chr(pad_byte) - pad_byte = pad_byte.encode('ascii') + pad_byte = bytes([pad_byte]) chunk += pad_byte * (BLOCK_SIZE - len(chunk)) done = True bytes_read += len(chunk) @@ -60,7 +73,7 @@ def encrypt_argparse(args): input_handle = open(args.input, 'rb') output_handle = open(args.output, 'wb') - password = hashit(args.password, 32) + password = hashit(args.password) initialization_vector = os.urandom(16) aes = AES.new(password, mode=3, IV=initialization_vector) output_handle.write(initialization_vector) @@ -71,15 +84,14 @@ def decrypt_argparse(args): input_handle = open(args.input, 'rb') output_handle = open(args.output, 'wb') - password = hashit(args.password, 32) + password = hashit(args.password) initialization_vector = input_handle.read(16) aes = AES.new(password, mode=3, IV=initialization_vector) decrypt_file(aes, input_handle, output_handle) -def hashit(text, length=None): +def hashit(text): h = hashlib.sha512(text.encode('utf-8')).hexdigest() - if length is not None: - h = h[:length] + h = h[:BLOCK_SIZE] return h def main(argv): @@ -102,4 +114,4 @@ def main(argv): args.func(args) if __name__ == '__main__': - main(sys.argv[1:]) \ No newline at end of file + main(sys.argv[1:]) diff --git a/Downloady/downloady.py b/Downloady/downloady.py index 2d2d7ee..91c8246 100644 --- a/Downloady/downloady.py +++ b/Downloady/downloady.py @@ -23,7 +23,7 @@ FILENAME_BADCHARS = '*?"<>|\r' last_request = 0 CHUNKSIZE = 4 * bytestring.KIBIBYTE -TIMEOUT = 600 +TIMEOUT = 60 TEMP_EXTENSION = '.downloadytemp' PRINT_LIMITER = ratelimiter.Ratelimiter(allowance=5, mode='reject') @@ -35,11 +35,12 @@ def download_file( auth=None, bytespersecond=None, callback_progress=None, + do_head=True, headers=None, overwrite=False, raise_for_undersized=True, + timeout=None, verbose=False, - **get_kwargs ): headers = headers or {} @@ -61,13 +62,20 @@ def download_file( localname, auth=auth, bytespersecond=bytespersecond, + callback_progress=callback_progress, + do_head=do_head, headers=headers, overwrite=overwrite, + raise_for_undersized=raise_for_undersized, + timeout=timeout, ) #print(plan) if plan is None: return + return download_plan(plan) + +def download_plan(plan): localname = plan['download_into'] directory = os.path.split(localname)[0] if directory != '': @@ -77,7 +85,7 @@ def download_file( file_handle.seek(plan['seek_to']) if plan['header_range_min'] is not None: - headers['range'] = 'bytes={min}-{max}'.format( + plan['headers']['range'] = 'bytes={min}-{max}'.format( min=plan['header_range_min'], max=plan['header_range_max'], ) @@ -89,7 +97,20 @@ def download_file( else: bytes_downloaded = 0 - download_stream = request('get', url, stream=True, headers=headers, auth=auth, **get_kwargs) + download_stream = request( + 'get', + plan['url'], + stream=True, + auth=plan['auth'], + headers=plan['headers'], + timeout=plan['timeout'], + ) + + if plan['remote_total_bytes'] is None: + # Since we didn't do a head, let's fill this in now. + plan['remote_total_bytes'] = int(download_stream.headers.get('Content-Length', 0)) + + callback_progress = plan['callback_progress'] if callback_progress is not None: callback_progress = callback_progress(plan['remote_total_bytes']) @@ -108,7 +129,7 @@ def download_file( if os.devnull not in [localname, plan['real_localname']]: localsize = os.path.getsize(localname) undersized = plan['plan_type'] != 'partial' and localsize < plan['remote_total_bytes'] - if raise_for_undersized and undersized: + if plan['raise_for_undersized'] and undersized: message = 'File does not contain expected number of bytes. Received {size} / {total}' message = message.format(size=localsize, total=plan['remote_total_bytes']) raise Exception(message) @@ -121,12 +142,17 @@ def download_file( def prepare_plan( url, localname, - auth, - bytespersecond, - headers, - overwrite, + auth=None, + bytespersecond=None, + callback_progress=None, + do_head=True, + headers=None, + overwrite=False, + raise_for_undersized=True, + timeout=TIMEOUT, ): # Chapter 1: File existence + headers = headers or {} user_provided_range = 'range' in headers real_localname = localname temp_localname = localname + TEMP_EXTENSION @@ -163,21 +189,34 @@ def prepare_plan( temp_headers = headers temp_headers.update({'range': 'bytes=0-'}) - # I'm using a GET instead of an actual HEAD here because some servers respond - # differently, even though they're not supposed to. - head = request('get', url, stream=True, headers=temp_headers, auth=auth) - remote_total_bytes = int(head.headers.get('content-length', 0)) - server_respects_range = (head.status_code == 206 and 'content-range' in head.headers) - head.connection.close() + if do_head: + # I'm using a GET instead of an actual HEAD here because some servers respond + # differently, even though they're not supposed to. + head = request('get', url, stream=True, headers=temp_headers, auth=auth) + remote_total_bytes = int(head.headers.get('content-length', 0)) + server_respects_range = (head.status_code == 206 and 'content-range' in head.headers) + head.connection.close() + else: + remote_total_bytes = None + server_respects_range = False if user_provided_range and not server_respects_range: - raise Exception('Server did not respect your range header') + if not do_head: + raise Exception('Cannot determine range support without the head request') + else: + raise Exception('Server did not respect your range header') # Chapter 5: Plan definitions plan_base = { + 'url': url, + 'auth': auth, + 'callback_progress': callback_progress, 'limiter': limiter, + 'headers': headers, 'real_localname': real_localname, + 'raise_for_undersized': raise_for_undersized, 'remote_total_bytes': remote_total_bytes, + 'timeout': timeout, } plan_fulldownload = dict( plan_base, @@ -373,8 +412,10 @@ def download_argparse(args): localname=args.localname, bytespersecond=bytespersecond, callback_progress=callback, + do_head=args.no_head is False, headers=headers, overwrite=args.overwrite, + timeout=int(args.timeout), verbose=True, ) @@ -388,6 +429,8 @@ if __name__ == '__main__': parser.add_argument('-bps', '--bytespersecond', dest='bytespersecond', default=None) parser.add_argument('-ow', '--overwrite', dest='overwrite', action='store_true') parser.add_argument('-r', '--range', dest='range', default=None) + parser.add_argument('--timeout', dest='timeout', default=TIMEOUT) + parser.add_argument('--no-head', dest='no_head', action='store_true') parser.set_defaults(func=download_argparse) args = parser.parse_args() diff --git a/Pathclass/pathclass.py b/Pathclass/pathclass.py index f4600b6..9e2eb87 100644 --- a/Pathclass/pathclass.py +++ b/Pathclass/pathclass.py @@ -1,25 +1,25 @@ import glob import os + class Path: ''' I started to use pathlib.Path, but it was too much of a pain. ''' def __init__(self, path): - path = os.path.normpath(path) - path = os.path.abspath(path) - self.absolute_path = path + if isinstance(path, Path): + self.absolute_path = path.absolute_path + else: + path = os.path.normpath(path) + path = os.path.abspath(path) + self.absolute_path = path def __contains__(self, other): - this = os.path.normcase(self.absolute_path) - that = os.path.normcase(other.absolute_path) - return that.startswith(this) + return other.normcase.startswith(self.normcase) def __eq__(self, other): if not hasattr(other, 'absolute_path'): return False - this = os.path.normcase(self.absolute_path) - that = os.path.normcase(other.absolute_path) - return this == that + return self.normcase == other.normcase def __hash__(self): return hash(os.path.normcase(self.absolute_path)) @@ -35,6 +35,10 @@ class Path: self.absolute_path = get_path_casing(self.absolute_path) return self.absolute_path + @property + def depth(self): + return len(self.absolute_path.split(os.sep)) + @property def exists(self): return os.path.exists(self.absolute_path) @@ -51,6 +55,10 @@ class Path: def is_link(self): return os.path.islink(self.absolute_path) + @property + def normcase(self): + return os.path.normcase(self.absolute_path) + @property def parent(self): parent = os.path.dirname(self.absolute_path) @@ -59,10 +67,21 @@ class Path: @property def relative_path(self): - relative = self.absolute_path - relative = relative.replace(os.getcwd(), '') - relative = relative.lstrip(os.sep) - return relative + cwd = Path(os.getcwd()) + self.correct_case() + if self.absolute_path == cwd: + return '.' + + if self in cwd: + return self.absolute_path.replace(cwd.absolute_path, '.') + + common = common_path([os.getcwd(), self.absolute_path], fallback=None) + if common is None: + return self.absolute_path + backsteps = cwd.depth - common.depth + backsteps = os.sep.join('..' for x in range(backsteps)) + print('hi') + return self.absolute_path.replace(common.absolute_path, backsteps) @property def size(self): @@ -80,6 +99,34 @@ class Path: return Path(os.path.join(self.absolute_path, basename)) +def common_path(paths, fallback): + ''' + Given a list of file paths, determine the deepest path which all + have in common. + ''' + if isinstance(paths, (str, Path)): + raise TypeError('`paths` must be a collection') + paths = [Path(f) for f in paths] + + if len(paths) == 0: + raise ValueError('Empty list') + + if hasattr(paths, 'pop'): + model = paths.pop() + else: + model = paths[0] + paths = paths[1:] + + while True: + if all(f in model for f in paths): + return model + parent = model.parent + if parent == model: + # We just processed the root, and now we're stuck at the root. + # Which means there was no common path. + return fallback + model = parent + def get_path_casing(path): ''' Take what is perhaps incorrectly cased input and get the path's actual @@ -89,8 +136,21 @@ def get_path_casing(path): Ethan Furman http://stackoverflow.com/a/7133137/5430534 xvorsx http://stackoverflow.com/a/14742779/5430534 ''' - if isinstance(path, Path): - path = path.absolute_path + if not isinstance(path, Path): + path = Path(path) + + # Nonexistent paths don't glob correctly. If the input is a nonexistent + # subpath of an existing path, we have to glob the existing portion first, + # and then attach the fake portion again at the end. + input_path = path + while not path.exists: + parent = path.parent + if path == parent: + # We're stuck at a fake root. + return input_path.absolute_path + path = parent + + path = path.absolute_path (drive, subpath) = os.path.splitdrive(path) drive = drive.upper() @@ -99,11 +159,17 @@ def get_path_casing(path): pattern = [glob_patternize(piece) for piece in subpath.split(os.sep)] pattern = os.sep.join(pattern) pattern = drive + os.sep + pattern - #print(pattern) + try: - return glob.glob(pattern)[0] + cased = glob.glob(pattern)[0] + imaginary_portion = input_path.normcase + real_portion = os.path.normcase(cased) + imaginary_portion = imaginary_portion.replace(real_portion, '') + imaginary_portion = imaginary_portion.lstrip(os.sep) + cased = os.path.join(cased, imaginary_portion) + return cased except IndexError: - return path + return input_path def glob_patternize(piece): ''' @@ -111,10 +177,15 @@ def glob_patternize(piece): correct path name, while guaranteeing that the only result will be the correct path. Special cases are: - !, because in glob syntax, [!x] tells glob to look for paths that don't contain - "x". [!] is invalid syntax, so we pick the first non-! character to put - in the brackets. - [, because this starts a capture group + `!` + because in glob syntax, [!x] tells glob to look for paths that don't contain + "x", and [!] is invalid syntax. + `[`, `]` + because this starts a glob capture group + + so we pick the first non-special character to put in the brackets. + If the path consists entirely of these special characters, then the + casing doesn't need to be corrected anyway. ''' piece = glob.escape(piece) for character in piece: @@ -124,3 +195,13 @@ def glob_patternize(piece): piece = piece.replace(character, replacement, 1) break return piece + +def normalize_sep(path): + for char in ('\\', '/'): + if char != os.sep: + path = path.replace(char, os.sep) + path = path.rstrip(os.sep) + return path + +def system_root(): + return os.path.abspath(os.sep) diff --git a/Safeprint/safeprint.py b/Safeprint/safeprint.py new file mode 100644 index 0000000..51889d8 --- /dev/null +++ b/Safeprint/safeprint.py @@ -0,0 +1,18 @@ +''' +This function is slow and ugly, but I need a way to safely print unicode strings +on systems that don't support it without crippling those who do. +''' +def safeprint(text, file_handle=None): + for character in text: + try: + if file_handle: + file_handle.write(character) + else: + print(character, end='', flush=False) + except UnicodeError: + if file_handle: + file_handle.write('?') + else: + print('?', end='', flush=False) + if not file_handle: + print() diff --git a/ServerReference/simpleserver.py b/ServerReference/simpleserver.py index 605362f..36e71fb 100644 --- a/ServerReference/simpleserver.py +++ b/ServerReference/simpleserver.py @@ -290,7 +290,7 @@ def generate_random_filename(original_filename='', length=8): return identifier def main(): - server = ThreadedServer(('', 32768), RequestHandler) + server = ThreadedServer(('', int(sys.argv[1] or 32768)), RequestHandler) print('server starting') server.serve_forever() diff --git a/Steganographic/asciibet.txt b/Steganographic/asciibet.txt index 498f31c..821e789 100644 --- a/Steganographic/asciibet.txt +++ b/Steganographic/asciibet.txt @@ -1,1209 +1,1310 @@ - ########## -#### #### -#### ###### -#### ######## -#### ## #### -######## #### -###### #### -#### #### - ########## - - - - ## - #### -######## - #### - #### - #### - #### - #### -############ - - - - ######## -#### #### -#### #### - #### - #### - #### - #### -#### #### -############ - - - - ######## -#### #### - #### - #### - ###### - #### - #### -#### #### - ######## - - - - #### - ###### - ######## - #### #### -#### #### -############## - #### - #### - ######## - - - -############ -#### -#### -#### -########## - #### - #### -#### #### - ######## - - - - ###### - #### -#### -#### -########## -#### #### -#### #### -#### #### - ######## - - - -############## -#### #### -#### #### - #### - #### - #### - #### - #### - #### - - - - ######## -#### #### -#### #### -###### #### - ######## -#### ###### -#### #### -#### #### - ######## - - - - ######## -#### #### -#### #### -#### #### - ########## - #### - #### - #### - ###### - - - - - - - ######## - #### - ########## -#### #### -#### #### - ###### #### - - - -###### - #### - #### - ########## - #### #### - #### #### - #### #### - #### #### -#### ###### - - - - - - - ######## -#### #### -#### -#### -#### #### - ######## - - - - ###### - #### - #### - ########## -#### #### -#### #### -#### #### -#### #### - ###### #### - - - - - - - ######## -#### #### -############ -#### -#### #### - ######## - - - - ###### - #### #### - #### - #### -########## - #### - #### - #### -######## - - - - - - - ###### #### -#### #### -#### #### -#### #### - ########## - #### -#### #### - ######## - -###### - #### - #### - #### #### - ###### #### - #### #### - #### #### - #### #### -###### #### - - - - #### - #### - - ######## - #### - #### - #### - #### - ############ - - - - #### - #### - - ######## - #### - #### - #### - #### -#### #### -#### #### - ######## - -###### - #### - #### - #### #### - #### #### - ######## - #### #### - #### #### -###### #### - - - - ######## - #### - #### - #### - #### - #### - #### - #### - ############ - - - - - - -############ -#### ## #### -#### ## #### -#### ## #### -#### ## #### -#### #### - - - - - - -########## -#### #### -#### #### -#### #### -#### #### -#### #### - - - - - - - ######## -#### #### -#### #### -#### #### -#### #### - ######## - - - - - - -#### ###### - #### #### - #### #### - #### #### - #### #### - ########## - #### -######## - - - - - ###### #### -#### #### -#### #### -#### #### -#### #### - ########## - #### - ######## - - - - -###### #### - #### ###### - ###### #### - #### - #### -######## - - - - - - - ######## -#### #### - #### - #### -#### #### - ######## - - - - - ## - #### -############ - #### - #### - #### - #### #### - ###### - - - - - - -#### #### -#### #### -#### #### -#### #### -#### #### - ###### #### - - - - - - -#### #### -#### #### -#### #### -#### #### - ######## - #### - - - - - - -#### #### -#### #### -#### ## #### -#### ## #### - #### #### - #### #### - - - - - - -#### #### - #### #### - ###### - ###### - #### #### -#### #### - - - - - - - #### #### - #### #### - #### #### - #### #### - ######## - #### - #### -######## - - - - -############ -## #### - #### - #### -#### ## -############ - - - - #### - ######## -#### #### -#### #### -#### #### -############ -#### #### -#### #### -#### #### - - - -############ - #### #### - #### #### - #### #### - ########## - #### #### - #### #### - #### #### -############ - - - - ######## - #### #### -#### #### -#### -#### -#### -#### #### - #### #### - ######## - - - -########## - #### #### - #### #### - #### #### - #### #### - #### #### - #### #### - #### #### -########## - - - -############## - #### ## - #### - #### ## - ########## - #### ## - #### - #### ## -############## - - - -############## - #### #### - #### ## - #### ## - ########## - #### ## - #### - #### -######## - - - - ######## - #### #### -#### #### -#### -#### -#### ###### -#### #### - #### #### - ########## - - - -#### #### -#### #### -#### #### -#### #### -############ -#### #### -#### #### -#### #### -#### #### - - - - ######## - #### - #### - #### - #### - #### - #### - #### - ######## - - - - ######## - #### - #### - #### - #### -#### #### -#### #### -#### #### - ######## - - - -###### #### - #### #### - #### #### - #### #### - ######## - #### #### - #### #### - #### #### -###### #### - - - -######## - #### - #### - #### - #### - #### ## - #### #### - #### #### -############## - - - -#### #### -###### ###### -############## -############## -#### ## #### -#### #### -#### #### -#### #### -#### #### - - - -#### #### -#### #### -###### #### -######## #### -############## -#### ######## -#### ###### -#### #### -#### #### - - - - ###### - #### #### -#### #### -#### #### -#### #### -#### #### -#### #### - #### #### - ###### - - - -############ - #### #### - #### #### - #### #### - ########## - #### - #### - #### -######## - - - - ###### - #### #### -#### #### -#### #### -#### #### -#### ###### -#### ######## - ########## - #### - ######## - - -############ - #### #### - #### #### - #### #### - ########## - #### #### - #### #### - #### #### -###### #### - - - - ######## -#### #### -#### #### -#### - ###### - #### -#### #### -#### #### - ######## - - - -############ -## #### ## - #### - #### - #### - #### - #### - #### - ######## - - - -#### #### -#### #### -#### #### -#### #### -#### #### -#### #### -#### #### -#### #### - ######## - - - -#### #### -#### #### -#### #### -#### #### -#### #### -#### #### -#### #### - ######## - #### - - - -#### #### -#### #### -#### #### -#### #### -#### ## #### -#### ## #### - #### #### - #### #### - #### #### - - - -#### #### -#### #### -#### #### - ######## - #### - ######## -#### #### -#### #### -#### #### - - - -#### #### -#### #### -#### #### -#### #### - ######## - #### - #### - #### - ######## - - - -############## -#### ###### -## #### - #### - #### - #### - #### ## -#### #### -############## - - - - #### - ######## - ######## - ######## - #### - #### - - #### - #### - - - - #### #### - #### #### - #### #### - ## ## - - - - - - - - - #### #### - #### #### -############## - #### #### - #### #### - #### #### -############## - #### #### - #### #### - - - #### - #### - ########## -#### -#### - ######## - #### - #### -########## - #### - #### - - - - -#### ## -#### #### - #### - #### - #### -#### #### -## #### - - - - ###### -#### #### -#### #### - ###### -########## ## -#### ######## -#### #### -#### ###### - ###### #### - - - - #### - #### - #### - #### - - - - - - - - - #### - #### - #### - #### - #### - #### - #### - #### - #### - - - - #### - #### - #### - #### - #### - #### - #### - #### - #### - - - - - - #### #### - ######## -################ - ######## - #### #### - - - - - - - - #### - #### - ############ - #### - #### - - - - - - - - - - - - - ###### - ###### - #### - - - - - - -############## - - - - - - - - - - - - - - - ###### - ###### - - - - - ## - #### - #### - #### - #### - #### -#### -## - - - - - - ###### - ###### - - - ###### - ###### - - - - - - - ###### - ###### - - - ###### - ###### - #### - #### - - - #### - #### - #### - #### -#### - #### - #### - #### - #### - - - - - - - ############ - - ############ - - - - - - - #### - #### - #### - #### - #### - #### - #### - #### - #### - - - - ######## -#### #### - #### - #### - #### - #### - - #### - #### - - - - ########## -#### #### -#### #### -#### ######## -#### ######## -#### ######## -#### -#### - ########## - - - - ######## - #### - #### - #### - #### - #### - #### - #### - ######## - - - - -## -#### - #### - #### - #### - #### - #### - ## - - - - ######## - #### - #### - #### - #### - #### - #### - #### - ######## - - - ## - ###### - #### #### -#### #### - - - - - - - - - - - - - - - - - - -################ - - #### - #### - #### - - - - - - - - - - - ###### - #### - #### - #### -#### - #### - #### - #### - ###### - - - - #### - #### - #### - #### - - #### - #### - #### - #### - - - -###### - #### - #### - #### - #### - #### - #### - #### -###### - - - - ###### #### -#### #### ## -#### ###### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ########## - ###### - ###### ## - ###### ## -########## -#### #### -#### #### -#### #### - ######## - - - - ######## - #### #### - #### #### - #### #### - ######## - #### - ############ - #### - #### \ No newline at end of file +--------------------- +! ########## ! +! #### #### ! +! #### ###### ! +! #### ######## ! +! #### ## #### ! +! ######## #### ! +! ###### #### ! +! #### #### ! +! ########## ! +! ! +! ! +! ! +--------------------- +! ## ! +! #### ! +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ############ ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### #### ! +! ############ ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### ! +! #### ! +! ###### ! +! #### ! +! #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! #### ! +! ###### ! +! ######## ! +! #### #### ! +! #### #### ! +! ############## ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ############ ! +! #### ! +! #### ! +! #### ! +! ########## ! +! #### ! +! #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! #### ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ############## ! +! #### #### ! +! #### #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ######## ! +! #### ###### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### ! +! #### ! +! #### ! +! ###### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ######## ! +! #### ! +! ########## ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### ###### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ######## ! +! #### #### ! +! #### ! +! #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ######## ! +! #### #### ! +! ############ ! +! #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### #### ! +! #### ! +! #### ! +! ########## ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ###### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### ! +! #### #### ! +! ######## ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! #### #### ! +! ###### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! ! +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! ############ ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! ! +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ############ ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ############ ! +! #### ## #### ! +! #### ## #### ! +! #### ## #### ! +! #### ## #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ######## ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! #### ###### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### ! +! ######## ! +! ! +--------------------- +! ! +! ! +! ! +! ###### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### ! +! ######## ! +! ! +--------------------- +! ! +! ! +! ! +! ###### #### ! +! #### ###### ! +! ###### #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ######## ! +! #### #### ! +! #### ! +! #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ! +! ## ! +! #### ! +! ############ ! +! #### ! +! #### ! +! #### ! +! #### #### ! +! ###### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! #### #### ! +! #### #### ! +! #### ## #### ! +! #### ## #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! #### #### ! +! #### #### ! +! ###### ! +! ###### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### ! +! #### ! +! ######## ! +! ! +--------------------- +! ! +! ! +! ! +! ############ ! +! ## #### ! +! #### ! +! #### ! +! #### ## ! +! ############ ! +! ! +! ! +! ! +--------------------- +! #### ! +! ######## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ############ ! +! #### #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ############ ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ############ ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! #### ! +! #### ! +! #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! ! +! ! +! ! +--------------------- +! ############## ! +! #### ## ! +! #### ! +! #### ## ! +! ########## ! +! #### ## ! +! #### ! +! #### ## ! +! ############## ! +! ! +! ! +! ! +--------------------- +! ############## ! +! #### #### ! +! #### ## ! +! #### ## ! +! ########## ! +! #### ## ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! #### ! +! #### ! +! #### ###### ! +! #### #### ! +! #### #### ! +! ########## ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ############ ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ###### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ## ! +! #### #### ! +! #### #### ! +! ############## ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! ###### ###### ! +! ############## ! +! ############## ! +! #### ## #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! ###### #### ! +! ######## #### ! +! ############## ! +! #### ######## ! +! #### ###### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ###### ! +! ! +! ! +! ! +--------------------- +! ############ ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### ###### ! +! #### ######## ! +! ########## ! +! #### ! +! ######## ! +! ! +! ! +--------------------- +! ############ ! +! #### #### ! +! #### #### ! +! #### #### ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! #### ! +! ###### ! +! #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ############ ! +! ## #### ## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! #### ## #### ! +! #### ## #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### ! +! ######## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ############## ! +! #### ###### ! +! ## #### ! +! #### ! +! #### ! +! #### ! +! #### ## ! +! #### #### ! +! ############## ! +! ! +! ! +! ! +--------------------- +! #### ! +! ######## ! +! ######## ! +! ######## ! +! #### ! +! #### ! +! ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! #### #### ! +! ## ## ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! #### #### ! +! #### #### ! +! ############## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ############## ! +! #### #### ! +! #### #### ! +! ! +! ! +! #### ! +--------------------- +! #### ! +! ########## ! +! #### ! +! #### ! +! ######## ! +! #### ! +! #### ! +! ########## ! +! #### ! +! #### ! +! ! +! ! +--------------------- +! ! +! ! +! #### ## ! +! #### #### ! +! #### ! +! #### ! +! #### ! +! #### #### ! +! ## #### ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### #### ! +! #### #### ! +! ###### ! +! ########## ## ! +! #### ######## ! +! #### #### ! +! #### ###### ! +! ###### #### ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! #### #### ! +! ######## ! +! ################ ! +! ######## ! +! #### #### ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! #### ! +! #### ! +! ############ ! +! #### ! +! #### ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ###### ! +! ###### ! +! #### ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ############## ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ###### ! +! ###### ! +! ! +! ! +! ! +--------------------- +! ! +! ## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ## ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ###### ! +! ###### ! +! ! +! ! +! ###### ! +! ###### ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ###### ! +! ###### ! +! ! +! ! +! ###### ! +! ###### ! +! #### ! +! #### ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ############ ! +! ! +! ############ ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! ########## ! +! #### #### ! +! #### #### ! +! #### ######## ! +! #### ######## ! +! #### ######## ! +! #### ! +! #### ! +! ########## ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ! +! ## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ## ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ######## ! +! ! +! ! +! ## ! +--------------------- +! ###### ! +! #### #### ! +! #### #### ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ################ ! +! ! +! #### ! +--------------------- +! #### ! +! #### ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ###### ! +! ! +! ! +! ! +--------------------- +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! #### ! +! #### ! +! #### ! +! #### ! +! ! +! ! +! ! +--------------------- +! ###### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! #### ! +! ###### ! +! ! +! ! +! ! +--------------------- +! ###### #### ! +! #### #### ## ! +! #### ###### ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +! ! +--------------------- +! ########## ! +! ###### ! +! ###### ## ! +! ###### ## ! +! ########## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! ! +! ! +! ! +--------------------- +! ######## ! +! #### #### ! +! #### #### ! +! #### #### ! +! ######## ! +! #### ! +! ############ ! +! #### ! +! #### ! diff --git a/Templates/timetest.py b/Templates/timetest.py new file mode 100644 index 0000000..6e0a755 --- /dev/null +++ b/Templates/timetest.py @@ -0,0 +1,22 @@ +import time + +LOOPS = 10000 + +def func_1(): + pass + +def func_2(): + pass + + +start = time.time() +for x in range(LOOPS): + func_1() +end = time.time() +print('v1', end - start) + +start = time.time() +for x in range(LOOPS): + func_2() +end = time.time() +print('v2', end - start) diff --git a/ThreadedDL/threaded_dl.py b/ThreadedDL/threaded_dl.py index a091f4a..7285ef3 100644 --- a/ThreadedDL/threaded_dl.py +++ b/ThreadedDL/threaded_dl.py @@ -36,6 +36,10 @@ def threaded_dl(urls, thread_count, filename_format=None): if filename_format is None: filename_format = '{now}_{index}_{basename}' filename_format = filename_format.replace('{index}', '{index:0%0dd}' % index_digits) + if '{' not in filename_format and len(urls) > 1: + filename_format += '_{index}' + if '{extension}' not in filename_format: + filename_format += '{extension}' now = int(time.time()) for (index, url) in enumerate(urls): while len(threads) == thread_count: diff --git a/Toolbox/brename.py b/Toolbox/brename.py index 9172002..f2170dc 100644 --- a/Toolbox/brename.py +++ b/Toolbox/brename.py @@ -16,6 +16,8 @@ import random import re import sys +from voussoirkit import safeprint + def brename(transformation): old = os.listdir() @@ -46,7 +48,7 @@ def loop(pairs, dry=False): line = '{old}\n{new}\n' line = line.format(old=x, new=y) #print(line.encode('utf-8')) - print(line.encode('ascii', 'replace').decode()) + safeprint.safeprint(line) has_content = True else: os.rename(x, y) diff --git a/Toolbox/fileswith.py b/Toolbox/fileswith.py index 2d2011a..598a4bd 100644 --- a/Toolbox/fileswith.py +++ b/Toolbox/fileswith.py @@ -10,6 +10,7 @@ import glob import re import sys +from voussoirkit import safeprint from voussoirkit import spinal filepattern = sys.argv[1] @@ -31,5 +32,5 @@ for filename in spinal.walk_generator(): pass if matches: print(filename) - print('\n'.join(matches).encode('ascii', 'replace').decode()) + safeprint.safeprint('\n'.join(matches)) print() diff --git a/Toolbox/touch.py b/Toolbox/touch.py index a7c6db2..91339d2 100644 --- a/Toolbox/touch.py +++ b/Toolbox/touch.py @@ -5,14 +5,16 @@ import glob import os import sys +from voussoirkit import safeprint + def touch(glob_pattern): filenames = glob.glob(glob_pattern) if len(filenames) == 0: - print(glob_pattern.encode('ascii', 'replace').decode()) + safeprint.safeprint(glob_pattern) open(glob_pattern, 'a').close() else: for filename in filenames: - print(filename.encode('ascii', 'replace').decode()) + safeprint.safeprint(filename) os.utime(filename) if __name__ == '__main__': diff --git a/VoxelSphereGenerator/dot_corner.png b/VoxelSphereGenerator/dot_corner.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca0cbadcbd0e1efdf778cbb4b56b76ee0f6c11e GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sDEfH31!Z9ZwBpslBiV~B-+@*jQ{9ubBOj6FR)0eYDXDdxgc ze9asR5B{$zHSpyyFuTWcIi&F53dY534F?oXatD}6HZ(FaOgpT=k-)8f6KD{Fr>mdK II;Vst0KLpCQ~&?~ literal 0 HcmV?d00001 diff --git a/VoxelSphereGenerator/dot_highlight.png b/VoxelSphereGenerator/dot_highlight.png new file mode 100644 index 0000000000000000000000000000000000000000..242713bb0b1ed3216e6163340887237f50a64162 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1en(ALw%F~q_@`42w}j|jsC#-5&@0KH6x6m#Jz zzGe=E2csoL4SYE`>~}IRTluIdklBrwq0w=Y_<|fG24-f47G?zwIVs_Opg|0tu6{1- HoD!Mh=>2M0vg2N>FVdQ I&MBb@0KaN0tpET3 literal 0 HcmV?d00001 diff --git a/VoxelSphereGenerator/voxelspheregenerator.py b/VoxelSphereGenerator/voxelspheregenerator.py new file mode 100644 index 0000000..241c92a --- /dev/null +++ b/VoxelSphereGenerator/voxelspheregenerator.py @@ -0,0 +1,274 @@ +import argparse +import math +import PIL.Image +import PIL.ImageDraw +import sys + +def choose_guideline_style(guideline_mod): + if guideline_mod % 16 == 0: + return ('#1f32ff', 3) + if guideline_mod % 8 == 0: + return ('#80f783', 2) + if guideline_mod % 4 == 0: + return ('#f4bffb', 1) + +def voxelspheregenerator(WIDTH, HEIGH, DEPTH, WALL_THICKNESS=None): + def in_ellipsoid(x, y, z, rad_x, rad_y, rad_z, center_x=None, center_y=None, center_z=None): + ''' + Given a point (x, y, z), return whether that point lies inside the + ellipsoid defined by (x/a)^2 + (y/b)^2 + (z/c)^2 = 1 + ''' + if center_x is None: center_x = rad_x + if center_y is None: center_y = rad_y + if center_z is None: center_z = rad_z + #print(x, y, z, rad_x, rad_y, rad_z, center_x, center_y, center_z) + x = ((x - center_x) / rad_x) ** 2 + y = ((y - center_y) / rad_y) ** 2 + z = ((z - center_z) / rad_z) ** 2 + distance = x + y + z + #print(distance) + return distance < 1 + + ODD_W = WIDTH % 2 == 1 + ODD_H = HEIGH % 2 == 1 + ODD_D = DEPTH % 2 == 1 + + RAD_X = WIDTH / 2 + RAD_Y = HEIGH / 2 + RAD_Z = DEPTH / 2 + + if WALL_THICKNESS: + INNER_RAD_X = RAD_X - WALL_THICKNESS + INNER_RAD_Y = RAD_Y - WALL_THICKNESS + INNER_RAD_Z = RAD_Z - WALL_THICKNESS + + X_CENTER = {WIDTH // 2} if ODD_W else {WIDTH // 2, (WIDTH // 2) - 1} + Y_CENTER = {HEIGH // 2} if ODD_H else {HEIGH // 2, (HEIGH // 2) - 1} + Z_CENTER = {DEPTH // 2} if ODD_D else {DEPTH // 2, (DEPTH // 2) - 1} + + layer_digits = len(str(DEPTH)) + filename_form = '{w}x{h}x{d}w{wall}-{{layer:0{digits}}}.png' + filename_form = filename_form.format( + w=WIDTH, + h=HEIGH, + d=DEPTH, + wall=WALL_THICKNESS if WALL_THICKNESS else 0, + digits=layer_digits, + ) + + dot_highlight = PIL.Image.open('dot_highlight.png') + dot_normal = PIL.Image.open('dot_normal.png') + dot_corner = PIL.Image.open('dot_corner.png') + pixel_scale = dot_highlight.size[0] + + # Space between each pixel + PIXEL_MARGIN = 7 + + # Space between the pixel area and the canvas + PIXELSPACE_MARGIN = 20 + + # Space between the canvas area and the image edge + CANVAS_MARGIN = 20 + + LABEL_HEIGH = 20 + FINAL_IMAGE_SCALE = 1 + + PIXELSPACE_WIDTH = (WIDTH * pixel_scale) + ((WIDTH - 1) * PIXEL_MARGIN) + PIXELSPACE_HEIGH = (HEIGH * pixel_scale) + ((HEIGH - 1) * PIXEL_MARGIN) + + CANVAS_WIDTH = PIXELSPACE_WIDTH + (2 * PIXELSPACE_MARGIN) + CANVAS_HEIGH = PIXELSPACE_HEIGH + (2 * PIXELSPACE_MARGIN) + + IMAGE_WIDTH = CANVAS_WIDTH + (2 * CANVAS_MARGIN) + IMAGE_HEIGH = CANVAS_HEIGH + (2 * CANVAS_MARGIN) + LABEL_HEIGH + + CANVAS_START_X = CANVAS_MARGIN + CANVAS_START_Y = CANVAS_MARGIN + CANVAS_END_X = CANVAS_START_X + CANVAS_WIDTH + CANVAS_END_Y = CANVAS_START_Y + CANVAS_HEIGH + + PIXELSPACE_START_X = CANVAS_START_X + PIXELSPACE_MARGIN + PIXELSPACE_START_Y = CANVAS_START_Y + PIXELSPACE_MARGIN + PIXELSPACE_END_X = PIXELSPACE_START_X + PIXELSPACE_WIDTH + PIXELSPACE_END_Y = PIXELSPACE_START_Y + PIXELSPACE_HEIGH + + GUIDELINE_MOD_X = math.ceil(RAD_X) + GUIDELINE_MOD_Y = math.ceil(RAD_Y) + + def pixel_coord(x, y): + x = PIXELSPACE_START_X + (x * pixel_scale) + (x * PIXEL_MARGIN) + y = PIXELSPACE_START_Y + (y * pixel_scale) + (y * PIXEL_MARGIN) + return (x, y) + + def make_layer_matrix(z): + layer_matrix = [[None for y in range(math.ceil(RAD_Y))] for x in range(math.ceil(RAD_X))] + + # Generate the upper left corner. + furthest_x = RAD_X + furthest_y = RAD_Y + for y in range(math.ceil(RAD_Y)): + for x in range(math.ceil(RAD_X)): + ux = x + 0.5 + uy = y + 0.5 + uz = z + 0.5 + + within = in_ellipsoid(ux, uy, uz, RAD_X, RAD_Y, RAD_Z) + if WALL_THICKNESS: + in_hole = in_ellipsoid( + ux, uy, uz, + INNER_RAD_X, INNER_RAD_Y, INNER_RAD_Z, + RAD_X, RAD_Y, RAD_Z + ) + within = within and not in_hole + if within: + if x in X_CENTER or y in Y_CENTER: + if z in Z_CENTER: + dot = dot_normal + else: + dot = dot_highlight + else: + if z in Z_CENTER: + dot = dot_highlight + else: + dot = dot_normal + layer_matrix[x][y] = dot + furthest_x = min(x, furthest_x) + furthest_y = min(y, furthest_y) + #layer_image.paste(dot, box=(pixel_coord_x, pixel_coord_y)) + + # Mark the corner pieces + for y in range(furthest_y, math.ceil(RAD_Y-1)): + for x in range(furthest_x, math.ceil(RAD_X-1)): + is_corner = ( + layer_matrix[x][y] is not None and + layer_matrix[x-1][y+1] is not None and + layer_matrix[x+1][y-1] is not None and + ( + # Outer corners + (layer_matrix[x][y-1] is None and layer_matrix[x-1][y] is None) or + # Inner corners, if hollow + (layer_matrix[x][y+1] is None and layer_matrix[x+1][y] is None) + ) + ) + if is_corner: + layer_matrix[x][y] = dot_corner + + return layer_matrix + + def make_layer_image(layer_matrix): + layer_image = PIL.Image.new('RGBA', size=(IMAGE_WIDTH, IMAGE_HEIGH), color=(0, 0, 0, 0)) + draw = PIL.ImageDraw.ImageDraw(layer_image) + + # Plot. + for y in range(math.ceil(RAD_Y)): + for x in range(math.ceil(RAD_X)): + right_x = (WIDTH - 1) - x + bottom_y = (HEIGH - 1) - y + if layer_matrix[x][y] is not None: + layer_image.paste(layer_matrix[x][y], box=pixel_coord(x, y)) + layer_image.paste(layer_matrix[x][y], box=pixel_coord(right_x, y)) + layer_image.paste(layer_matrix[x][y], box=pixel_coord(x, bottom_y)) + layer_image.paste(layer_matrix[x][y], box=pixel_coord(right_x, bottom_y)) + + # To draw the guidelines, start from + for x in range(GUIDELINE_MOD_X % 4, WIDTH + 4, 4): + # Vertical guideline + as_if = GUIDELINE_MOD_X - x + #print(x, as_if) + line_x = PIXELSPACE_START_X + (x * pixel_scale) + (x * PIXEL_MARGIN) + line_x = line_x - PIXEL_MARGIN + (PIXEL_MARGIN // 2) + if line_x >= PIXELSPACE_END_X: + continue + (color, width) = choose_guideline_style(as_if) + draw.line((line_x, CANVAS_START_Y, line_x, CANVAS_END_Y - 1), fill=color, width=width) + draw.text((line_x, CANVAS_END_X), str(x), fill='#000') + + for y in range(GUIDELINE_MOD_Y % 4, HEIGH + 4, 4): + # Horizontal guideline + as_if = GUIDELINE_MOD_Y - y + #print(y, as_if) + line_y = PIXELSPACE_START_Y + (y * pixel_scale) + (y * PIXEL_MARGIN) + line_y = line_y - PIXEL_MARGIN + (PIXEL_MARGIN // 2) + if line_y >= PIXELSPACE_END_Y: + continue + (color, width) = choose_guideline_style(as_if) + draw.line((CANVAS_START_X, line_y, CANVAS_END_X - 1, line_y), fill=color, width=width) + draw.text((CANVAS_END_X, line_y), str(y), fill='#000') + + draw.rectangle((CANVAS_START_X, CANVAS_START_Y, CANVAS_END_X - 1, CANVAS_END_Y - 1), outline='#000') + draw.text((CANVAS_START_X, IMAGE_HEIGH - LABEL_HEIGH), layer_filename, fill='#000') + print(layer_filename) + if FINAL_IMAGE_SCALE != 1: + layer_image = layer_image.resize((FINAL_IMAGE_SCALE * IMAGE_WIDTH, FINAL_IMAGE_SCALE * IMAGE_HEIGH)) + + return layer_image + + layer_matrices = [] + for z in range(DEPTH): + if z < math.ceil(RAD_Z): + layer_matrix = make_layer_matrix(z) + layer_matrices.append(layer_matrix) + else: + layer_matrix = layer_matrices[(DEPTH - 1) - z] + layer_filename = filename_form.format(layer=z) + layer_image = make_layer_image(layer_matrix) + layer_image.save(layer_filename) + + + # Copy to the upper right corner. + #for y in range(math.ceil(RAD_Y)): + # #print(y) + # for x in range(math.ceil(RAD_X), WIDTH): + # #print(x, '==', (WIDTH-1) - x) + # mapped_x = (WIDTH - 1) - x + # layer_matrix[x][y] = layer_matrix[mapped_x][y] + + # Copy to the lower semicircle. + #for y in range(math.ceil(RAD_Y), HEIGH): + # #print(y) + # for x in range(WIDTH): + # #print(y, '==', (HEIGH-1) - y) + # mapped_y = (HEIGH-1) - y + # layer_matrix[x][y] = layer_matrix[x][mapped_y] + + + + #break + #layer_matrix = [['▓' if dot == dot_highlight else '░' if dot == dot_normal else ' 'for dot in sublist] for sublist in layer_matrix] + #layer_matrix = [''.join(sublist) for sublist in layer_matrix] + #layer_matrix = '\n'.join(layer_matrix) + #print(layer_matrix) + #print() + + +def voxelsphere_argparse(args): + height_depth_match = bool(args.height) == bool(args.depth) + if not height_depth_match: + raise ValueError('Must provide both or neither of height+depth. Not just one.') + + if (args.height is args.depth is None): + args.height = args.width + args.depth = args.width + + voxelspheregenerator( + int(args.width), + int(args.height), + int(args.depth), + WALL_THICKNESS=int(args.wall_thickness) if args.wall_thickness else None, + ) + + +def main(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('width') + parser.add_argument('height', nargs='?', default=None) + parser.add_argument('depth', nargs='?', default=None) + parser.add_argument('--wall', dest='wall_thickness', default=None) + parser.set_defaults(func=voxelsphere_argparse) + + args = parser.parse_args(argv) + args.func(args) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/WebstreamZip/README.md b/WebstreamZip/README.md new file mode 100644 index 0000000..e111313 --- /dev/null +++ b/WebstreamZip/README.md @@ -0,0 +1,3 @@ +PLEASE DON'T USE THIS + +Use [python-zipstream](https://github.com/allanlei/python-zipstream) instead. diff --git a/WebstreamZip/test.zip b/WebstreamZip/test.zip deleted file mode 100644 index a2f3957e3b473c1cc8c409c83224a8787686301e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeIvu};G<5C-7PmHH01vC!BlRfkB7Aa#I&7eJ;lwIPYE^QEXfJq{J6VytMV{}xBd zpS#?b&aa)ZZP_)&*-GbHE|&An*k#A8i}} diff --git a/WebstreamZip/tester.py b/WebstreamZip/tester.py deleted file mode 100644 index f158cfb..0000000 --- a/WebstreamZip/tester.py +++ /dev/null @@ -1,7 +0,0 @@ -import webstreamzip - -g = webstreamzip.stream_zip({'C:\\git\\else\\readme.md':'michael.md'}) - -x = open('test.zip', 'wb') -for chunk in g: - x.write(chunk) \ No newline at end of file diff --git a/_voussoirkit/voussoirkit.py b/_voussoirkit/voussoirkit.py index 184afb9..41fcf97 100644 --- a/_voussoirkit/voussoirkit.py +++ b/_voussoirkit/voussoirkit.py @@ -10,6 +10,7 @@ PATHS = [ 'C:\\git\\else\\Pathclass\\pathclass.py', 'C:\\git\\else\\Ratelimiter\\ratelimiter.py', 'C:\\git\\else\\RateMeter\\ratemeter.py', +'C:\\git\\else\\Safeprint\\safeprint.py', 'C:\\git\\else\\SpinalTap\\spinal.py', 'C:\\git\\else\\WebstreamZip\\webstreamzip.py', ] @@ -40,7 +41,7 @@ import setuptools setuptools.setup( author='voussoir', name='{package}', - version='0.0.3', + version='0.0.4', description='', py_modules=[{py_modules}], ) diff --git a/_voussoirkit/voussoirkit.zip b/_voussoirkit/voussoirkit.zip index dad85249147cd86ca509d172de97d05d96d57249..635ae9a46c9a0dab49fd806a8c723ac89f221eb7 100644 GIT binary patch delta 7866 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 delta 6534 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