master
Ethan Dalool 2016-12-22 19:42:21 -08:00
parent 6e64483523
commit 0896ba96e6
20 changed files with 1826 additions and 1269 deletions

View File

@ -9,10 +9,24 @@ import os
from voussoirkit import bytestring from voussoirkit import bytestring
# 128 bits
BLOCK_SIZE = 32 BLOCK_SIZE = 32
KEY_SIZE = 32
SEEK_END = 2 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): def decrypt_file(aes, input_handle, output_handle):
current_pos = input_handle.tell() current_pos = input_handle.tell()
input_size = input_handle.seek(0, SEEK_END) - current_pos 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] last_byte = chunk[-1]
if len(chunk) < BLOCK_SIZE: if len(chunk) < BLOCK_SIZE:
pad_byte = (last_byte + 1) % 256 pad_byte = (last_byte + 1) % 256
pad_byte = chr(pad_byte) pad_byte = bytes([pad_byte])
pad_byte = pad_byte.encode('ascii')
chunk += pad_byte * (BLOCK_SIZE - len(chunk)) chunk += pad_byte * (BLOCK_SIZE - len(chunk))
done = True done = True
bytes_read += len(chunk) bytes_read += len(chunk)
@ -60,7 +73,7 @@ def encrypt_argparse(args):
input_handle = open(args.input, 'rb') input_handle = open(args.input, 'rb')
output_handle = open(args.output, 'wb') output_handle = open(args.output, 'wb')
password = hashit(args.password, 32) password = hashit(args.password)
initialization_vector = os.urandom(16) initialization_vector = os.urandom(16)
aes = AES.new(password, mode=3, IV=initialization_vector) aes = AES.new(password, mode=3, IV=initialization_vector)
output_handle.write(initialization_vector) output_handle.write(initialization_vector)
@ -71,15 +84,14 @@ def decrypt_argparse(args):
input_handle = open(args.input, 'rb') input_handle = open(args.input, 'rb')
output_handle = open(args.output, 'wb') output_handle = open(args.output, 'wb')
password = hashit(args.password, 32) password = hashit(args.password)
initialization_vector = input_handle.read(16) initialization_vector = input_handle.read(16)
aes = AES.new(password, mode=3, IV=initialization_vector) aes = AES.new(password, mode=3, IV=initialization_vector)
decrypt_file(aes, input_handle, output_handle) decrypt_file(aes, input_handle, output_handle)
def hashit(text, length=None): def hashit(text):
h = hashlib.sha512(text.encode('utf-8')).hexdigest() h = hashlib.sha512(text.encode('utf-8')).hexdigest()
if length is not None: h = h[:BLOCK_SIZE]
h = h[:length]
return h return h
def main(argv): def main(argv):
@ -102,4 +114,4 @@ def main(argv):
args.func(args) args.func(args)
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv[1:]) main(sys.argv[1:])

View File

@ -23,7 +23,7 @@ FILENAME_BADCHARS = '*?"<>|\r'
last_request = 0 last_request = 0
CHUNKSIZE = 4 * bytestring.KIBIBYTE CHUNKSIZE = 4 * bytestring.KIBIBYTE
TIMEOUT = 600 TIMEOUT = 60
TEMP_EXTENSION = '.downloadytemp' TEMP_EXTENSION = '.downloadytemp'
PRINT_LIMITER = ratelimiter.Ratelimiter(allowance=5, mode='reject') PRINT_LIMITER = ratelimiter.Ratelimiter(allowance=5, mode='reject')
@ -35,11 +35,12 @@ def download_file(
auth=None, auth=None,
bytespersecond=None, bytespersecond=None,
callback_progress=None, callback_progress=None,
do_head=True,
headers=None, headers=None,
overwrite=False, overwrite=False,
raise_for_undersized=True, raise_for_undersized=True,
timeout=None,
verbose=False, verbose=False,
**get_kwargs
): ):
headers = headers or {} headers = headers or {}
@ -61,13 +62,20 @@ def download_file(
localname, localname,
auth=auth, auth=auth,
bytespersecond=bytespersecond, bytespersecond=bytespersecond,
callback_progress=callback_progress,
do_head=do_head,
headers=headers, headers=headers,
overwrite=overwrite, overwrite=overwrite,
raise_for_undersized=raise_for_undersized,
timeout=timeout,
) )
#print(plan) #print(plan)
if plan is None: if plan is None:
return return
return download_plan(plan)
def download_plan(plan):
localname = plan['download_into'] localname = plan['download_into']
directory = os.path.split(localname)[0] directory = os.path.split(localname)[0]
if directory != '': if directory != '':
@ -77,7 +85,7 @@ def download_file(
file_handle.seek(plan['seek_to']) file_handle.seek(plan['seek_to'])
if plan['header_range_min'] is not None: 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'], min=plan['header_range_min'],
max=plan['header_range_max'], max=plan['header_range_max'],
) )
@ -89,7 +97,20 @@ def download_file(
else: else:
bytes_downloaded = 0 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: if callback_progress is not None:
callback_progress = callback_progress(plan['remote_total_bytes']) callback_progress = callback_progress(plan['remote_total_bytes'])
@ -108,7 +129,7 @@ def download_file(
if os.devnull not in [localname, plan['real_localname']]: if os.devnull not in [localname, plan['real_localname']]:
localsize = os.path.getsize(localname) localsize = os.path.getsize(localname)
undersized = plan['plan_type'] != 'partial' and localsize < plan['remote_total_bytes'] 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 = 'File does not contain expected number of bytes. Received {size} / {total}'
message = message.format(size=localsize, total=plan['remote_total_bytes']) message = message.format(size=localsize, total=plan['remote_total_bytes'])
raise Exception(message) raise Exception(message)
@ -121,12 +142,17 @@ def download_file(
def prepare_plan( def prepare_plan(
url, url,
localname, localname,
auth, auth=None,
bytespersecond, bytespersecond=None,
headers, callback_progress=None,
overwrite, do_head=True,
headers=None,
overwrite=False,
raise_for_undersized=True,
timeout=TIMEOUT,
): ):
# Chapter 1: File existence # Chapter 1: File existence
headers = headers or {}
user_provided_range = 'range' in headers user_provided_range = 'range' in headers
real_localname = localname real_localname = localname
temp_localname = localname + TEMP_EXTENSION temp_localname = localname + TEMP_EXTENSION
@ -163,21 +189,34 @@ def prepare_plan(
temp_headers = headers temp_headers = headers
temp_headers.update({'range': 'bytes=0-'}) temp_headers.update({'range': 'bytes=0-'})
# I'm using a GET instead of an actual HEAD here because some servers respond if do_head:
# differently, even though they're not supposed to. # I'm using a GET instead of an actual HEAD here because some servers respond
head = request('get', url, stream=True, headers=temp_headers, auth=auth) # differently, even though they're not supposed to.
remote_total_bytes = int(head.headers.get('content-length', 0)) head = request('get', url, stream=True, headers=temp_headers, auth=auth)
server_respects_range = (head.status_code == 206 and 'content-range' in head.headers) remote_total_bytes = int(head.headers.get('content-length', 0))
head.connection.close() 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: 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 # Chapter 5: Plan definitions
plan_base = { plan_base = {
'url': url,
'auth': auth,
'callback_progress': callback_progress,
'limiter': limiter, 'limiter': limiter,
'headers': headers,
'real_localname': real_localname, 'real_localname': real_localname,
'raise_for_undersized': raise_for_undersized,
'remote_total_bytes': remote_total_bytes, 'remote_total_bytes': remote_total_bytes,
'timeout': timeout,
} }
plan_fulldownload = dict( plan_fulldownload = dict(
plan_base, plan_base,
@ -373,8 +412,10 @@ def download_argparse(args):
localname=args.localname, localname=args.localname,
bytespersecond=bytespersecond, bytespersecond=bytespersecond,
callback_progress=callback, callback_progress=callback,
do_head=args.no_head is False,
headers=headers, headers=headers,
overwrite=args.overwrite, overwrite=args.overwrite,
timeout=int(args.timeout),
verbose=True, verbose=True,
) )
@ -388,6 +429,8 @@ if __name__ == '__main__':
parser.add_argument('-bps', '--bytespersecond', dest='bytespersecond', default=None) parser.add_argument('-bps', '--bytespersecond', dest='bytespersecond', default=None)
parser.add_argument('-ow', '--overwrite', dest='overwrite', action='store_true') parser.add_argument('-ow', '--overwrite', dest='overwrite', action='store_true')
parser.add_argument('-r', '--range', dest='range', default=None) 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) parser.set_defaults(func=download_argparse)
args = parser.parse_args() args = parser.parse_args()

View File

@ -1,25 +1,25 @@
import glob import glob
import os import os
class Path: class Path:
''' '''
I started to use pathlib.Path, but it was too much of a pain. I started to use pathlib.Path, but it was too much of a pain.
''' '''
def __init__(self, path): def __init__(self, path):
path = os.path.normpath(path) if isinstance(path, Path):
path = os.path.abspath(path) self.absolute_path = path.absolute_path
self.absolute_path = path else:
path = os.path.normpath(path)
path = os.path.abspath(path)
self.absolute_path = path
def __contains__(self, other): def __contains__(self, other):
this = os.path.normcase(self.absolute_path) return other.normcase.startswith(self.normcase)
that = os.path.normcase(other.absolute_path)
return that.startswith(this)
def __eq__(self, other): def __eq__(self, other):
if not hasattr(other, 'absolute_path'): if not hasattr(other, 'absolute_path'):
return False return False
this = os.path.normcase(self.absolute_path) return self.normcase == other.normcase
that = os.path.normcase(other.absolute_path)
return this == that
def __hash__(self): def __hash__(self):
return hash(os.path.normcase(self.absolute_path)) return hash(os.path.normcase(self.absolute_path))
@ -35,6 +35,10 @@ class Path:
self.absolute_path = get_path_casing(self.absolute_path) self.absolute_path = get_path_casing(self.absolute_path)
return self.absolute_path return self.absolute_path
@property
def depth(self):
return len(self.absolute_path.split(os.sep))
@property @property
def exists(self): def exists(self):
return os.path.exists(self.absolute_path) return os.path.exists(self.absolute_path)
@ -51,6 +55,10 @@ class Path:
def is_link(self): def is_link(self):
return os.path.islink(self.absolute_path) return os.path.islink(self.absolute_path)
@property
def normcase(self):
return os.path.normcase(self.absolute_path)
@property @property
def parent(self): def parent(self):
parent = os.path.dirname(self.absolute_path) parent = os.path.dirname(self.absolute_path)
@ -59,10 +67,21 @@ class Path:
@property @property
def relative_path(self): def relative_path(self):
relative = self.absolute_path cwd = Path(os.getcwd())
relative = relative.replace(os.getcwd(), '') self.correct_case()
relative = relative.lstrip(os.sep) if self.absolute_path == cwd:
return relative 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 @property
def size(self): def size(self):
@ -80,6 +99,34 @@ class Path:
return Path(os.path.join(self.absolute_path, basename)) 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): def get_path_casing(path):
''' '''
Take what is perhaps incorrectly cased input and get the path's actual 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 Ethan Furman http://stackoverflow.com/a/7133137/5430534
xvorsx http://stackoverflow.com/a/14742779/5430534 xvorsx http://stackoverflow.com/a/14742779/5430534
''' '''
if isinstance(path, Path): if not isinstance(path, Path):
path = path.absolute_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, subpath) = os.path.splitdrive(path)
drive = drive.upper() drive = drive.upper()
@ -99,11 +159,17 @@ def get_path_casing(path):
pattern = [glob_patternize(piece) for piece in subpath.split(os.sep)] pattern = [glob_patternize(piece) for piece in subpath.split(os.sep)]
pattern = os.sep.join(pattern) pattern = os.sep.join(pattern)
pattern = drive + os.sep + pattern pattern = drive + os.sep + pattern
#print(pattern)
try: 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: except IndexError:
return path return input_path
def glob_patternize(piece): 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. correct path name, while guaranteeing that the only result will be the correct path.
Special cases are: 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 because in glob syntax, [!x] tells glob to look for paths that don't contain
in the brackets. "x", and [!] is invalid syntax.
[, because this starts a capture group `[`, `]`
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) piece = glob.escape(piece)
for character in piece: for character in piece:
@ -124,3 +195,13 @@ def glob_patternize(piece):
piece = piece.replace(character, replacement, 1) piece = piece.replace(character, replacement, 1)
break break
return piece 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)

18
Safeprint/safeprint.py Normal file
View File

@ -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()

View File

@ -290,7 +290,7 @@ def generate_random_filename(original_filename='', length=8):
return identifier return identifier
def main(): def main():
server = ThreadedServer(('', 32768), RequestHandler) server = ThreadedServer(('', int(sys.argv[1] or 32768)), RequestHandler)
print('server starting') print('server starting')
server.serve_forever() server.serve_forever()

File diff suppressed because it is too large Load Diff

22
Templates/timetest.py Normal file
View File

@ -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)

View File

@ -36,6 +36,10 @@ def threaded_dl(urls, thread_count, filename_format=None):
if filename_format is None: if filename_format is None:
filename_format = '{now}_{index}_{basename}' filename_format = '{now}_{index}_{basename}'
filename_format = filename_format.replace('{index}', '{index:0%0dd}' % index_digits) 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()) now = int(time.time())
for (index, url) in enumerate(urls): for (index, url) in enumerate(urls):
while len(threads) == thread_count: while len(threads) == thread_count:

View File

@ -16,6 +16,8 @@ import random
import re import re
import sys import sys
from voussoirkit import safeprint
def brename(transformation): def brename(transformation):
old = os.listdir() old = os.listdir()
@ -46,7 +48,7 @@ def loop(pairs, dry=False):
line = '{old}\n{new}\n' line = '{old}\n{new}\n'
line = line.format(old=x, new=y) line = line.format(old=x, new=y)
#print(line.encode('utf-8')) #print(line.encode('utf-8'))
print(line.encode('ascii', 'replace').decode()) safeprint.safeprint(line)
has_content = True has_content = True
else: else:
os.rename(x, y) os.rename(x, y)

View File

@ -10,6 +10,7 @@ import glob
import re import re
import sys import sys
from voussoirkit import safeprint
from voussoirkit import spinal from voussoirkit import spinal
filepattern = sys.argv[1] filepattern = sys.argv[1]
@ -31,5 +32,5 @@ for filename in spinal.walk_generator():
pass pass
if matches: if matches:
print(filename) print(filename)
print('\n'.join(matches).encode('ascii', 'replace').decode()) safeprint.safeprint('\n'.join(matches))
print() print()

View File

@ -5,14 +5,16 @@ import glob
import os import os
import sys import sys
from voussoirkit import safeprint
def touch(glob_pattern): def touch(glob_pattern):
filenames = glob.glob(glob_pattern) filenames = glob.glob(glob_pattern)
if len(filenames) == 0: if len(filenames) == 0:
print(glob_pattern.encode('ascii', 'replace').decode()) safeprint.safeprint(glob_pattern)
open(glob_pattern, 'a').close() open(glob_pattern, 'a').close()
else: else:
for filename in filenames: for filename in filenames:
print(filename.encode('ascii', 'replace').decode()) safeprint.safeprint(filename)
os.utime(filename) os.utime(filename)
if __name__ == '__main__': if __name__ == '__main__':

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View File

@ -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:])

3
WebstreamZip/README.md Normal file
View File

@ -0,0 +1,3 @@
PLEASE DON'T USE THIS
Use [python-zipstream](https://github.com/allanlei/python-zipstream) instead.

Binary file not shown.

View File

@ -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)

View File

@ -10,6 +10,7 @@ PATHS = [
'C:\\git\\else\\Pathclass\\pathclass.py', 'C:\\git\\else\\Pathclass\\pathclass.py',
'C:\\git\\else\\Ratelimiter\\ratelimiter.py', 'C:\\git\\else\\Ratelimiter\\ratelimiter.py',
'C:\\git\\else\\RateMeter\\ratemeter.py', 'C:\\git\\else\\RateMeter\\ratemeter.py',
'C:\\git\\else\\Safeprint\\safeprint.py',
'C:\\git\\else\\SpinalTap\\spinal.py', 'C:\\git\\else\\SpinalTap\\spinal.py',
'C:\\git\\else\\WebstreamZip\\webstreamzip.py', 'C:\\git\\else\\WebstreamZip\\webstreamzip.py',
] ]
@ -40,7 +41,7 @@ import setuptools
setuptools.setup( setuptools.setup(
author='voussoir', author='voussoir',
name='{package}', name='{package}',
version='0.0.3', version='0.0.4',
description='', description='',
py_modules=[{py_modules}], py_modules=[{py_modules}],
) )

Binary file not shown.